@orpc/openapi 0.0.0-next.e7d7758 → 0.0.0-next.e7ee5a9

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 (58) hide show
  1. package/README.md +91 -0
  2. package/dist/adapters/fetch/index.d.mts +14 -0
  3. package/dist/adapters/fetch/index.d.ts +14 -0
  4. package/dist/adapters/fetch/index.mjs +10 -0
  5. package/dist/adapters/hono/index.d.mts +7 -0
  6. package/dist/adapters/hono/index.d.ts +7 -0
  7. package/dist/adapters/hono/index.mjs +10 -0
  8. package/dist/adapters/next/index.d.mts +7 -0
  9. package/dist/adapters/next/index.d.ts +7 -0
  10. package/dist/adapters/next/index.mjs +10 -0
  11. package/dist/adapters/node/index.d.mts +14 -0
  12. package/dist/adapters/node/index.d.ts +14 -0
  13. package/dist/adapters/node/index.mjs +29 -0
  14. package/dist/adapters/standard/index.d.mts +24 -0
  15. package/dist/adapters/standard/index.d.ts +24 -0
  16. package/dist/adapters/standard/index.mjs +7 -0
  17. package/dist/index.d.mts +169 -0
  18. package/dist/index.d.ts +169 -0
  19. package/dist/{index.js → index.mjs} +271 -100
  20. package/dist/shared/openapi.BHG_gu5Z.mjs +8 -0
  21. package/dist/shared/openapi.CDsfPHgw.mjs +148 -0
  22. package/dist/shared/openapi.D0VMNR6V.mjs +25 -0
  23. package/dist/shared/openapi.Dz_6xooR.d.mts +7 -0
  24. package/dist/shared/openapi.Dz_6xooR.d.ts +7 -0
  25. package/package.json +35 -28
  26. package/dist/chunk-KNYXLM77.js +0 -107
  27. package/dist/chunk-YXHH6XHB.js +0 -642
  28. package/dist/fetch.js +0 -46
  29. package/dist/node.js +0 -46
  30. package/dist/src/adapters/fetch/bracket-notation.d.ts +0 -84
  31. package/dist/src/adapters/fetch/index.d.ts +0 -10
  32. package/dist/src/adapters/fetch/input-structure-compact.d.ts +0 -6
  33. package/dist/src/adapters/fetch/input-structure-detailed.d.ts +0 -11
  34. package/dist/src/adapters/fetch/openapi-handler-server.d.ts +0 -7
  35. package/dist/src/adapters/fetch/openapi-handler-serverless.d.ts +0 -7
  36. package/dist/src/adapters/fetch/openapi-handler.d.ts +0 -33
  37. package/dist/src/adapters/fetch/openapi-payload-codec.d.ts +0 -15
  38. package/dist/src/adapters/fetch/openapi-procedure-matcher.d.ts +0 -19
  39. package/dist/src/adapters/fetch/schema-coercer.d.ts +0 -10
  40. package/dist/src/adapters/node/index.d.ts +0 -5
  41. package/dist/src/adapters/node/openapi-handler-server.d.ts +0 -7
  42. package/dist/src/adapters/node/openapi-handler-serverless.d.ts +0 -7
  43. package/dist/src/adapters/node/openapi-handler.d.ts +0 -12
  44. package/dist/src/adapters/node/types.d.ts +0 -2
  45. package/dist/src/index.d.ts +0 -12
  46. package/dist/src/json-serializer.d.ts +0 -5
  47. package/dist/src/openapi-content-builder.d.ts +0 -10
  48. package/dist/src/openapi-error.d.ts +0 -3
  49. package/dist/src/openapi-generator.d.ts +0 -60
  50. package/dist/src/openapi-input-structure-parser.d.ts +0 -22
  51. package/dist/src/openapi-output-structure-parser.d.ts +0 -18
  52. package/dist/src/openapi-parameters-builder.d.ts +0 -12
  53. package/dist/src/openapi-path-parser.d.ts +0 -8
  54. package/dist/src/openapi.d.ts +0 -3
  55. package/dist/src/schema-converter.d.ts +0 -16
  56. package/dist/src/schema-utils.d.ts +0 -11
  57. package/dist/src/schema.d.ts +0 -12
  58. package/dist/src/utils.d.ts +0 -18
@@ -1,16 +1,61 @@
1
- import {
2
- JSONSerializer,
3
- forEachAllContractProcedure,
4
- forEachContractProcedure,
5
- standardizeHTTPPath
6
- } from "./chunk-KNYXLM77.js";
1
+ import { isProcedure, eachAllContractProcedure } from '@orpc/server';
2
+ import { OpenApiBuilder } from 'openapi3-ts/oas31';
3
+ export { OpenApiBuilder } from 'openapi3-ts/oas31';
4
+ import { findDeepMatches, isObject, get, omit, group } from '@orpc/shared';
5
+ import { fallbackORPCErrorStatus } from '@orpc/client';
6
+ import { fallbackContractConfig, getEventIteratorSchemaDetails } from '@orpc/contract';
7
+ import { OpenAPIJsonSerializer } from '@orpc/openapi-client/standard';
8
+ import * as draft202012 from 'json-schema-typed/draft-2020-12';
9
+ export { draft202012 as JSONSchema };
10
+ export { Format as JSONSchemaFormat } from 'json-schema-typed/draft-2020-12';
11
+ import { t as toOpenAPI31RoutePattern } from './shared/openapi.BHG_gu5Z.mjs';
12
+ export { s as standardizeHTTPPath } from './shared/openapi.BHG_gu5Z.mjs';
7
13
 
8
- // src/openapi.ts
9
- import { OpenApiBuilder } from "openapi3-ts/oas31";
14
+ const OPERATION_EXTENDER_SYMBOL = Symbol("ORPC_OPERATION_EXTENDER");
15
+ function setOperationExtender(o, extend) {
16
+ return new Proxy(o, {
17
+ get(target, prop, receiver) {
18
+ if (prop === OPERATION_EXTENDER_SYMBOL) {
19
+ return extend;
20
+ }
21
+ return Reflect.get(target, prop, receiver);
22
+ }
23
+ });
24
+ }
25
+ function getOperationExtender(o) {
26
+ return o[OPERATION_EXTENDER_SYMBOL];
27
+ }
28
+ function extendOperation(operation, procedure) {
29
+ const operationExtenders = [];
30
+ for (const errorItem of Object.values(procedure["~orpc"].errorMap)) {
31
+ const maybeExtender = getOperationExtender(errorItem);
32
+ if (maybeExtender) {
33
+ operationExtenders.push(maybeExtender);
34
+ }
35
+ }
36
+ if (isProcedure(procedure)) {
37
+ for (const middleware of procedure["~orpc"].middlewares) {
38
+ const maybeExtender = getOperationExtender(middleware);
39
+ if (maybeExtender) {
40
+ operationExtenders.push(maybeExtender);
41
+ }
42
+ }
43
+ }
44
+ let currentOperation = operation;
45
+ for (const extender of operationExtenders) {
46
+ if (typeof extender === "function") {
47
+ currentOperation = extender(currentOperation, procedure);
48
+ } else {
49
+ currentOperation = {
50
+ ...currentOperation,
51
+ ...extender
52
+ };
53
+ }
54
+ }
55
+ return currentOperation;
56
+ }
10
57
 
11
- // src/openapi-content-builder.ts
12
- import { findDeepMatches } from "@orpc/shared";
13
- var OpenAPIContentBuilder = class {
58
+ class OpenAPIContentBuilder {
14
59
  constructor(schemaUtils) {
15
60
  this.schemaUtils = schemaUtils;
16
61
  }
@@ -33,22 +78,20 @@ var OpenAPIContentBuilder = class {
33
78
  }
34
79
  return content;
35
80
  }
36
- };
81
+ }
37
82
 
38
- // src/openapi-error.ts
39
- var OpenAPIError = class extends Error {
40
- };
83
+ class OpenAPIError extends Error {
84
+ }
41
85
 
42
- // src/openapi-input-structure-parser.ts
43
- var OpenAPIInputStructureParser = class {
86
+ class OpenAPIInputStructureParser {
44
87
  constructor(schemaConverter, schemaUtils, pathParser) {
45
88
  this.schemaConverter = schemaConverter;
46
89
  this.schemaUtils = schemaUtils;
47
90
  this.pathParser = pathParser;
48
91
  }
49
92
  parse(contract, structure) {
50
- const inputSchema = this.schemaConverter.convert(contract["~orpc"].InputSchema, { strategy: "input" });
51
- const method = contract["~orpc"].route?.method ?? "POST";
93
+ const inputSchema = this.schemaConverter.convert(contract["~orpc"].inputSchema, { strategy: "input" });
94
+ const method = fallbackContractConfig("defaultMethod", contract["~orpc"].route?.method);
52
95
  const httpPath = contract["~orpc"].route?.path;
53
96
  if (this.schemaUtils.isAnySchema(inputSchema)) {
54
97
  return {
@@ -131,16 +174,15 @@ var OpenAPIInputStructureParser = class {
131
174
  bodySchema: method !== "GET" ? rest : void 0
132
175
  };
133
176
  }
134
- };
177
+ }
135
178
 
136
- // src/openapi-output-structure-parser.ts
137
- var OpenAPIOutputStructureParser = class {
179
+ class OpenAPIOutputStructureParser {
138
180
  constructor(schemaConverter, schemaUtils) {
139
181
  this.schemaConverter = schemaConverter;
140
182
  this.schemaUtils = schemaUtils;
141
183
  }
142
184
  parse(contract, structure) {
143
- const outputSchema = this.schemaConverter.convert(contract["~orpc"].OutputSchema, { strategy: "output" });
185
+ const outputSchema = this.schemaConverter.convert(contract["~orpc"].outputSchema, { strategy: "output" });
144
186
  if (this.schemaUtils.isAnySchema(outputSchema)) {
145
187
  return {
146
188
  headersSchema: void 0,
@@ -176,17 +218,15 @@ var OpenAPIOutputStructureParser = class {
176
218
  bodySchema: outputSchema
177
219
  };
178
220
  }
179
- };
221
+ }
180
222
 
181
- // src/openapi-parameters-builder.ts
182
- import { get, isPlainObject, omit } from "@orpc/shared";
183
- var OpenAPIParametersBuilder = class {
223
+ class OpenAPIParametersBuilder {
184
224
  build(paramIn, jsonSchema, options) {
185
225
  const parameters = [];
186
226
  for (const name in jsonSchema.properties) {
187
227
  const schema = jsonSchema.properties[name];
188
228
  const paramExamples = jsonSchema.examples?.filter((example) => {
189
- return isPlainObject(example) && name in example;
229
+ return isObject(example) && name in example;
190
230
  }).map((example) => {
191
231
  return example[name];
192
232
  });
@@ -214,10 +254,9 @@ var OpenAPIParametersBuilder = class {
214
254
  }
215
255
  return headersObject;
216
256
  }
217
- };
257
+ }
218
258
 
219
- // src/openapi-path-parser.ts
220
- var OpenAPIPathParser = class {
259
+ class OpenAPIPathParser {
221
260
  parseDynamicParams(path) {
222
261
  const raws = path.match(/\{([^}]+)\}/g) ?? [];
223
262
  return raws.map((raw) => {
@@ -225,10 +264,9 @@ var OpenAPIPathParser = class {
225
264
  return { name, raw };
226
265
  });
227
266
  }
228
- };
267
+ }
229
268
 
230
- // src/schema-converter.ts
231
- var CompositeSchemaConverter = class {
269
+ class CompositeSchemaConverter {
232
270
  converters;
233
271
  constructor(converters) {
234
272
  this.converters = converters;
@@ -244,15 +282,9 @@ var CompositeSchemaConverter = class {
244
282
  }
245
283
  return {};
246
284
  }
247
- };
248
-
249
- // src/schema-utils.ts
250
- import { isPlainObject as isPlainObject2 } from "@orpc/shared";
285
+ }
251
286
 
252
- // src/schema.ts
253
- import * as JSONSchema from "json-schema-typed/draft-2020-12";
254
- import { Format } from "json-schema-typed/draft-2020-12";
255
- var NON_LOGIC_KEYWORDS = [
287
+ const NON_LOGIC_KEYWORDS = [
256
288
  // Core Documentation Keywords
257
289
  "$anchor",
258
290
  "$comment",
@@ -280,23 +312,22 @@ var NON_LOGIC_KEYWORDS = [
280
312
  "$dynamicRef"
281
313
  ];
282
314
 
283
- // src/schema-utils.ts
284
- var SchemaUtils = class {
315
+ class SchemaUtils {
285
316
  isFileSchema(schema) {
286
- return typeof schema === "object" && schema.type === "string" && typeof schema.contentMediaType === "string";
317
+ return isObject(schema) && schema.type === "string" && typeof schema.contentMediaType === "string";
287
318
  }
288
319
  isObjectSchema(schema) {
289
- return typeof schema === "object" && schema.type === "object";
320
+ return isObject(schema) && schema.type === "object";
290
321
  }
291
322
  isAnySchema(schema) {
292
- return schema === true || Object.keys(schema).length === 0;
323
+ return schema === true || Object.keys(schema).filter((key) => !NON_LOGIC_KEYWORDS.includes(key)).length === 0;
293
324
  }
294
325
  isUndefinableSchema(schema) {
295
326
  const [matches] = this.filterSchemaBranches(schema, (schema2) => {
296
327
  if (typeof schema2 === "boolean") {
297
328
  return schema2;
298
329
  }
299
- return Object.keys(schema2).length === 0;
330
+ return Object.keys(schema2).filter((key) => !NON_LOGIC_KEYWORDS.includes(key)).length === 0;
300
331
  });
301
332
  return matches.length > 0;
302
333
  }
@@ -309,7 +340,7 @@ var SchemaUtils = class {
309
340
  }, {});
310
341
  matched.required = schema.required?.filter((key) => separatedProperties.includes(key));
311
342
  matched.examples = schema.examples?.map((example) => {
312
- if (!isPlainObject2(example)) {
343
+ if (!isObject(example)) {
313
344
  return example;
314
345
  }
315
346
  return Object.entries(example).reduce((acc, [key, value]) => {
@@ -325,7 +356,7 @@ var SchemaUtils = class {
325
356
  }, {});
326
357
  rest.required = schema.required?.filter((key) => !separatedProperties.includes(key));
327
358
  rest.examples = schema.examples?.map((example) => {
328
- if (!isPlainObject2(example)) {
359
+ if (!isObject(example)) {
329
360
  return example;
330
361
  }
331
362
  return Object.entries(example).reduce((acc, [key, value]) => {
@@ -365,10 +396,9 @@ var SchemaUtils = class {
365
396
  }
366
397
  return [matches, schema];
367
398
  }
368
- };
399
+ }
369
400
 
370
- // src/openapi-generator.ts
371
- var OpenAPIGenerator = class {
401
+ class OpenAPIGenerator {
372
402
  contentBuilder;
373
403
  parametersBuilder;
374
404
  schemaConverter;
@@ -380,11 +410,12 @@ var OpenAPIGenerator = class {
380
410
  errorHandlerStrategy;
381
411
  ignoreUndefinedPathProcedures;
382
412
  considerMissingTagDefinitionAsError;
413
+ strictErrorResponses;
383
414
  constructor(options) {
384
415
  this.parametersBuilder = options?.parametersBuilder ?? new OpenAPIParametersBuilder();
385
416
  this.schemaConverter = new CompositeSchemaConverter(options?.schemaConverters ?? []);
386
417
  this.schemaUtils = options?.schemaUtils ?? new SchemaUtils();
387
- this.jsonSerializer = options?.jsonSerializer ?? new JSONSerializer();
418
+ this.jsonSerializer = options?.jsonSerializer ?? new OpenAPIJsonSerializer();
388
419
  this.contentBuilder = options?.contentBuilder ?? new OpenAPIContentBuilder(this.schemaUtils);
389
420
  this.pathParser = new OpenAPIPathParser();
390
421
  this.inputStructureParser = options?.inputStructureParser ?? new OpenAPIInputStructureParser(this.schemaConverter, this.schemaUtils, this.pathParser);
@@ -392,6 +423,7 @@ var OpenAPIGenerator = class {
392
423
  this.errorHandlerStrategy = options?.errorHandlerStrategy ?? "throw";
393
424
  this.ignoreUndefinedPathProcedures = options?.ignoreUndefinedPathProcedures ?? false;
394
425
  this.considerMissingTagDefinitionAsError = options?.considerMissingTagDefinitionAsError ?? false;
426
+ this.strictErrorResponses = options?.strictErrorResponses ?? true;
395
427
  }
396
428
  async generate(router, doc) {
397
429
  const builder = new OpenApiBuilder({
@@ -399,35 +431,186 @@ var OpenAPIGenerator = class {
399
431
  openapi: "3.1.1"
400
432
  });
401
433
  const rootTags = doc.tags?.map((tag) => tag.name) ?? [];
402
- await forEachAllContractProcedure(router, ({ contract, path }) => {
434
+ await eachAllContractProcedure({
435
+ path: [],
436
+ router
437
+ }, ({ contract, path }) => {
403
438
  try {
404
439
  const def = contract["~orpc"];
405
440
  if (this.ignoreUndefinedPathProcedures && def.route?.path === void 0) {
406
441
  return;
407
442
  }
408
- const method = def.route?.method ?? "POST";
409
- const httpPath = def.route?.path ? standardizeHTTPPath(def.route?.path) : `/${path.map(encodeURIComponent).join("/")}`;
410
- const { paramsSchema, querySchema, headersSchema, bodySchema } = this.inputStructureParser.parse(contract, def.route?.inputStructure ?? "compact");
411
- const { headersSchema: resHeadersSchema, bodySchema: resBodySchema } = this.outputStructureParser.parse(contract, def.route?.outputStructure ?? "compact");
412
- const params = paramsSchema ? this.parametersBuilder.build("path", paramsSchema, {
413
- required: true
414
- }) : [];
415
- const query = querySchema ? this.parametersBuilder.build("query", querySchema) : [];
416
- const headers = headersSchema ? this.parametersBuilder.build("header", headersSchema) : [];
417
- const parameters = [...params, ...query, ...headers];
418
- const requestBody = bodySchema !== void 0 ? {
419
- required: this.schemaUtils.isUndefinableSchema(bodySchema),
420
- content: this.contentBuilder.build(bodySchema)
421
- } : void 0;
422
- const successResponse = {
423
- description: "OK",
424
- content: resBodySchema !== void 0 ? this.contentBuilder.build(resBodySchema, {
425
- example: def.outputExample
426
- }) : void 0,
427
- headers: resHeadersSchema !== void 0 ? this.parametersBuilder.buildHeadersObject(resHeadersSchema, {
428
- example: def.outputExample
429
- }) : void 0
430
- };
443
+ const method = fallbackContractConfig("defaultMethod", def.route?.method);
444
+ const httpPath = def.route?.path ? toOpenAPI31RoutePattern(def.route?.path) : `/${path.map(encodeURIComponent).join("/")}`;
445
+ const { parameters, requestBody } = (() => {
446
+ const eventIteratorSchemaDetails = getEventIteratorSchemaDetails(def.inputSchema);
447
+ if (eventIteratorSchemaDetails) {
448
+ const requestBody3 = {
449
+ required: true,
450
+ content: {
451
+ "text/event-stream": {
452
+ schema: {
453
+ oneOf: [
454
+ {
455
+ type: "object",
456
+ properties: {
457
+ event: { type: "string", const: "message" },
458
+ data: this.schemaConverter.convert(eventIteratorSchemaDetails.yields, { strategy: "input" }),
459
+ id: { type: "string" },
460
+ retry: { type: "number" }
461
+ },
462
+ required: ["event", "data"]
463
+ },
464
+ {
465
+ type: "object",
466
+ properties: {
467
+ event: { type: "string", const: "done" },
468
+ data: this.schemaConverter.convert(eventIteratorSchemaDetails.returns, { strategy: "input" }),
469
+ id: { type: "string" },
470
+ retry: { type: "number" }
471
+ },
472
+ required: ["event", "data"]
473
+ },
474
+ {
475
+ type: "object",
476
+ properties: {
477
+ event: { type: "string", const: "error" },
478
+ data: {},
479
+ id: { type: "string" },
480
+ retry: { type: "number" }
481
+ },
482
+ required: ["event", "data"]
483
+ }
484
+ ]
485
+ }
486
+ }
487
+ }
488
+ };
489
+ return { requestBody: requestBody3, parameters: [] };
490
+ }
491
+ const inputStructure = fallbackContractConfig("defaultInputStructure", def.route?.inputStructure);
492
+ const { paramsSchema, querySchema, headersSchema, bodySchema } = this.inputStructureParser.parse(contract, inputStructure);
493
+ const params = paramsSchema ? this.parametersBuilder.build("path", paramsSchema, {
494
+ required: true
495
+ }) : [];
496
+ const query = querySchema ? this.parametersBuilder.build("query", querySchema) : [];
497
+ const headers = headersSchema ? this.parametersBuilder.build("header", headersSchema) : [];
498
+ const parameters2 = [...params, ...query, ...headers];
499
+ const requestBody2 = bodySchema !== void 0 ? {
500
+ required: this.schemaUtils.isUndefinableSchema(bodySchema),
501
+ content: this.contentBuilder.build(bodySchema)
502
+ } : void 0;
503
+ return { parameters: parameters2, requestBody: requestBody2 };
504
+ })();
505
+ const { responses } = (() => {
506
+ const eventIteratorSchemaDetails = getEventIteratorSchemaDetails(def.outputSchema);
507
+ if (eventIteratorSchemaDetails) {
508
+ const responses3 = {};
509
+ responses3[fallbackContractConfig("defaultSuccessStatus", def.route?.successStatus)] = {
510
+ description: fallbackContractConfig("defaultSuccessDescription", def.route?.successDescription),
511
+ content: {
512
+ "text/event-stream": {
513
+ schema: {
514
+ oneOf: [
515
+ {
516
+ type: "object",
517
+ properties: {
518
+ event: { type: "string", const: "message" },
519
+ data: this.schemaConverter.convert(eventIteratorSchemaDetails.yields, { strategy: "input" }),
520
+ id: { type: "string" },
521
+ retry: { type: "number" }
522
+ },
523
+ required: ["event", "data"]
524
+ },
525
+ {
526
+ type: "object",
527
+ properties: {
528
+ event: { type: "string", const: "done" },
529
+ data: this.schemaConverter.convert(eventIteratorSchemaDetails.returns, { strategy: "input" }),
530
+ id: { type: "string" },
531
+ retry: { type: "number" }
532
+ },
533
+ required: ["event", "data"]
534
+ },
535
+ {
536
+ type: "object",
537
+ properties: {
538
+ event: { type: "string", const: "error" },
539
+ data: {},
540
+ id: { type: "string" },
541
+ retry: { type: "number" }
542
+ },
543
+ required: ["event", "data"]
544
+ }
545
+ ]
546
+ }
547
+ }
548
+ }
549
+ };
550
+ return { responses: responses3 };
551
+ }
552
+ const outputStructure = fallbackContractConfig("defaultOutputStructure", def.route?.outputStructure);
553
+ const { headersSchema: resHeadersSchema, bodySchema: resBodySchema } = this.outputStructureParser.parse(contract, outputStructure);
554
+ const responses2 = {};
555
+ responses2[fallbackContractConfig("defaultSuccessStatus", def.route?.successStatus)] = {
556
+ description: fallbackContractConfig("defaultSuccessDescription", def.route?.successDescription),
557
+ content: resBodySchema !== void 0 ? this.contentBuilder.build(resBodySchema) : void 0,
558
+ headers: resHeadersSchema !== void 0 ? this.parametersBuilder.buildHeadersObject(resHeadersSchema) : void 0
559
+ };
560
+ return { responses: responses2 };
561
+ })();
562
+ const errors = group(Object.entries(def.errorMap ?? {}).filter(([_, config]) => config).map(([code, config]) => ({
563
+ ...config,
564
+ code,
565
+ status: fallbackORPCErrorStatus(code, config?.status)
566
+ })), (error) => error.status);
567
+ for (const status in errors) {
568
+ const configs = errors[status];
569
+ if (!configs || configs.length === 0) {
570
+ continue;
571
+ }
572
+ const schemas = configs.map(({ data, code, message }) => {
573
+ const json = {
574
+ type: "object",
575
+ properties: {
576
+ defined: { const: true },
577
+ code: { const: code },
578
+ status: { const: Number(status) },
579
+ message: { type: "string", default: message },
580
+ data: {}
581
+ },
582
+ required: ["defined", "code", "status", "message"]
583
+ };
584
+ if (data) {
585
+ const dataJson = this.schemaConverter.convert(data, { strategy: "output" });
586
+ json.properties.data = dataJson;
587
+ if (!this.schemaUtils.isUndefinableSchema(dataJson)) {
588
+ json.required.push("data");
589
+ }
590
+ }
591
+ return json;
592
+ });
593
+ if (this.strictErrorResponses) {
594
+ schemas.push({
595
+ type: "object",
596
+ properties: {
597
+ defined: { const: false },
598
+ code: { type: "string" },
599
+ status: { type: "number" },
600
+ message: { type: "string" },
601
+ data: {}
602
+ },
603
+ required: ["defined", "code", "status", "message"]
604
+ });
605
+ }
606
+ const contentSchema = schemas.length === 1 ? schemas[0] : {
607
+ oneOf: schemas
608
+ };
609
+ responses[status] = {
610
+ description: status,
611
+ content: this.contentBuilder.build(contentSchema)
612
+ };
613
+ }
431
614
  if (this.considerMissingTagDefinitionAsError && def.route?.tags) {
432
615
  const missingTag = def.route?.tags.find((tag) => !rootTags.includes(tag));
433
616
  if (missingTag !== void 0) {
@@ -444,12 +627,11 @@ var OpenAPIGenerator = class {
444
627
  operationId: path.join("."),
445
628
  parameters: parameters.length ? parameters : void 0,
446
629
  requestBody,
447
- responses: {
448
- [def.route?.successStatus ?? 200]: successResponse
449
- }
630
+ responses
450
631
  };
632
+ const extendedOperation = extendOperation(operation, contract);
451
633
  builder.addPath(httpPath, {
452
- [method.toLocaleLowerCase()]: operation
634
+ [method.toLocaleLowerCase()]: extendedOperation
453
635
  });
454
636
  } catch (e) {
455
637
  if (e instanceof OpenAPIError) {
@@ -468,23 +650,12 @@ var OpenAPIGenerator = class {
468
650
  }
469
651
  }
470
652
  });
471
- return this.jsonSerializer.serialize(builder.getSpec());
653
+ return this.jsonSerializer.serialize(builder.getSpec())[0];
472
654
  }
655
+ }
656
+
657
+ const oo = {
658
+ spec: setOperationExtender
473
659
  };
474
- export {
475
- CompositeSchemaConverter,
476
- JSONSchema,
477
- Format as JSONSchemaFormat,
478
- JSONSerializer,
479
- NON_LOGIC_KEYWORDS,
480
- OpenAPIContentBuilder,
481
- OpenAPIGenerator,
482
- OpenAPIParametersBuilder,
483
- OpenAPIPathParser,
484
- OpenApiBuilder,
485
- SchemaUtils,
486
- forEachAllContractProcedure,
487
- forEachContractProcedure,
488
- standardizeHTTPPath
489
- };
490
- //# sourceMappingURL=index.js.map
660
+
661
+ export { CompositeSchemaConverter, NON_LOGIC_KEYWORDS, OpenAPIContentBuilder, OpenAPIGenerator, OpenAPIParametersBuilder, OpenAPIPathParser, SchemaUtils, extendOperation, getOperationExtender, oo, setOperationExtender, toOpenAPI31RoutePattern };
@@ -0,0 +1,8 @@
1
+ function standardizeHTTPPath(path) {
2
+ return `/${path.replace(/\/{2,}/g, "/").replace(/^\/|\/$/g, "")}`;
3
+ }
4
+ function toOpenAPI31RoutePattern(path) {
5
+ return standardizeHTTPPath(path).replace(/\{\+([^}]+)\}/g, "{$1}");
6
+ }
7
+
8
+ export { standardizeHTTPPath as s, toOpenAPI31RoutePattern as t };
@@ -0,0 +1,148 @@
1
+ import { fallbackContractConfig } from '@orpc/contract';
2
+ import { OpenAPISerializer } from '@orpc/openapi-client/standard';
3
+ import { isObject } from '@orpc/shared';
4
+ import { eachContractProcedure, convertPathToHttpPath, isProcedure, getLazyRouterPrefix, unlazy, getRouterChild, createContractedProcedure } from '@orpc/server';
5
+ import { createRouter, addRoute, findRoute } from 'rou3';
6
+ import { s as standardizeHTTPPath } from './openapi.BHG_gu5Z.mjs';
7
+
8
+ class OpenAPICodec {
9
+ constructor(serializer = new OpenAPISerializer()) {
10
+ this.serializer = serializer;
11
+ }
12
+ async decode(request, params, procedure) {
13
+ const inputStructure = fallbackContractConfig("defaultInputStructure", procedure["~orpc"].route.inputStructure);
14
+ if (inputStructure === "compact") {
15
+ const data = request.method === "GET" ? this.serializer.deserialize(request.url.searchParams) : this.serializer.deserialize(await request.body());
16
+ if (data === void 0) {
17
+ return params;
18
+ }
19
+ if (isObject(data)) {
20
+ return {
21
+ ...params,
22
+ ...data
23
+ };
24
+ }
25
+ return data;
26
+ }
27
+ const deserializeSearchParams = () => {
28
+ return this.serializer.deserialize(request.url.searchParams);
29
+ };
30
+ return {
31
+ params,
32
+ get query() {
33
+ const value = deserializeSearchParams();
34
+ Object.defineProperty(this, "query", { value, writable: true });
35
+ return value;
36
+ },
37
+ set query(value) {
38
+ Object.defineProperty(this, "query", { value, writable: true });
39
+ },
40
+ headers: request.headers,
41
+ body: this.serializer.deserialize(await request.body())
42
+ };
43
+ }
44
+ encode(output, procedure) {
45
+ const successStatus = fallbackContractConfig("defaultSuccessStatus", procedure["~orpc"].route.successStatus);
46
+ const outputStructure = fallbackContractConfig("defaultOutputStructure", procedure["~orpc"].route.outputStructure);
47
+ if (outputStructure === "compact") {
48
+ return {
49
+ status: successStatus,
50
+ headers: {},
51
+ body: this.serializer.serialize(output)
52
+ };
53
+ }
54
+ if (!isObject(output)) {
55
+ throw new Error(
56
+ 'Invalid output structure for "detailed" output. Expected format: { body: any, headers?: Record<string, string | string[] | undefined> }'
57
+ );
58
+ }
59
+ return {
60
+ status: successStatus,
61
+ headers: output.headers ?? {},
62
+ body: this.serializer.serialize(output.body)
63
+ };
64
+ }
65
+ encodeError(error) {
66
+ return {
67
+ status: error.status,
68
+ headers: {},
69
+ body: this.serializer.serialize(error.toJSON())
70
+ };
71
+ }
72
+ }
73
+
74
+ class OpenAPIMatcher {
75
+ tree = createRouter();
76
+ pendingRouters = [];
77
+ init(router, path = []) {
78
+ const laziedOptions = eachContractProcedure({
79
+ router,
80
+ path
81
+ }, ({ path: path2, contract }) => {
82
+ const method = fallbackContractConfig("defaultMethod", contract["~orpc"].route.method);
83
+ const httpPath = contract["~orpc"].route.path ? toRou3Pattern(contract["~orpc"].route.path) : convertPathToHttpPath(path2);
84
+ if (isProcedure(contract)) {
85
+ addRoute(this.tree, method, httpPath, {
86
+ path: path2,
87
+ contract,
88
+ procedure: contract,
89
+ // this mean dev not used contract-first so we can used contract as procedure directly
90
+ router
91
+ });
92
+ } else {
93
+ addRoute(this.tree, method, httpPath, {
94
+ path: path2,
95
+ contract,
96
+ procedure: void 0,
97
+ router
98
+ });
99
+ }
100
+ });
101
+ this.pendingRouters.push(...laziedOptions.map((option) => ({
102
+ ...option,
103
+ httpPathPrefix: convertPathToHttpPath(option.path),
104
+ laziedPrefix: getLazyRouterPrefix(option.lazied)
105
+ })));
106
+ }
107
+ async match(method, pathname) {
108
+ if (this.pendingRouters.length) {
109
+ const newPendingRouters = [];
110
+ for (const pendingRouter of this.pendingRouters) {
111
+ if (!pendingRouter.laziedPrefix || pathname.startsWith(pendingRouter.laziedPrefix) || pathname.startsWith(pendingRouter.httpPathPrefix)) {
112
+ const { default: router } = await unlazy(pendingRouter.lazied);
113
+ this.init(router, pendingRouter.path);
114
+ } else {
115
+ newPendingRouters.push(pendingRouter);
116
+ }
117
+ }
118
+ this.pendingRouters = newPendingRouters;
119
+ }
120
+ const match = findRoute(this.tree, method, pathname);
121
+ if (!match) {
122
+ return void 0;
123
+ }
124
+ if (!match.data.procedure) {
125
+ const { default: maybeProcedure } = await unlazy(getRouterChild(match.data.router, ...match.data.path));
126
+ if (!isProcedure(maybeProcedure)) {
127
+ throw new Error(`
128
+ [Contract-First] Missing or invalid implementation for procedure at path: ${convertPathToHttpPath(match.data.path)}.
129
+ Ensure that the procedure is correctly defined and matches the expected contract.
130
+ `);
131
+ }
132
+ match.data.procedure = createContractedProcedure(match.data.contract, maybeProcedure);
133
+ }
134
+ return {
135
+ path: match.data.path,
136
+ procedure: match.data.procedure,
137
+ params: match.params ? decodeParams(match.params) : void 0
138
+ };
139
+ }
140
+ }
141
+ function toRou3Pattern(path) {
142
+ return standardizeHTTPPath(path).replace(/\{\+([^}]+)\}/g, "**:$1").replace(/\{([^}]+)\}/g, ":$1");
143
+ }
144
+ function decodeParams(params) {
145
+ return Object.fromEntries(Object.entries(params).map(([key, value]) => [key, decodeURIComponent(value)]));
146
+ }
147
+
148
+ export { OpenAPICodec as O, OpenAPIMatcher as a };