@orpc/openapi 0.0.0-next.00a3135 → 0.0.0-next.0165ee0
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 +91 -0
- package/dist/adapters/fetch/index.d.mts +14 -0
- package/dist/adapters/fetch/index.d.ts +14 -0
- package/dist/adapters/fetch/index.mjs +10 -0
- package/dist/adapters/hono/index.d.mts +7 -0
- package/dist/adapters/hono/index.d.ts +7 -0
- package/dist/adapters/hono/index.mjs +10 -0
- package/dist/adapters/next/index.d.mts +7 -0
- package/dist/adapters/next/index.d.ts +7 -0
- package/dist/adapters/next/index.mjs +10 -0
- package/dist/adapters/node/index.d.mts +14 -0
- package/dist/adapters/node/index.d.ts +14 -0
- package/dist/adapters/node/index.mjs +29 -0
- package/dist/adapters/standard/index.d.mts +24 -0
- package/dist/adapters/standard/index.d.ts +24 -0
- package/dist/adapters/standard/index.mjs +7 -0
- package/dist/index.d.mts +169 -0
- package/dist/index.d.ts +169 -0
- package/dist/{index.js → index.mjs} +271 -106
- package/dist/shared/openapi.BHG_gu5Z.mjs +8 -0
- package/dist/shared/openapi.CDsfPHgw.mjs +148 -0
- package/dist/shared/openapi.D0VMNR6V.mjs +25 -0
- package/dist/shared/openapi.Dz_6xooR.d.mts +7 -0
- package/dist/shared/openapi.Dz_6xooR.d.ts +7 -0
- package/package.json +35 -28
- package/dist/chunk-KNYXLM77.js +0 -107
- package/dist/chunk-WNX6GP4X.js +0 -652
- package/dist/fetch.js +0 -46
- package/dist/node.js +0 -46
- package/dist/src/adapters/fetch/bracket-notation.d.ts +0 -84
- package/dist/src/adapters/fetch/index.d.ts +0 -10
- package/dist/src/adapters/fetch/input-structure-compact.d.ts +0 -6
- package/dist/src/adapters/fetch/input-structure-detailed.d.ts +0 -11
- package/dist/src/adapters/fetch/openapi-handler-server.d.ts +0 -7
- package/dist/src/adapters/fetch/openapi-handler-serverless.d.ts +0 -7
- package/dist/src/adapters/fetch/openapi-handler.d.ts +0 -33
- package/dist/src/adapters/fetch/openapi-payload-codec.d.ts +0 -15
- package/dist/src/adapters/fetch/openapi-procedure-matcher.d.ts +0 -19
- package/dist/src/adapters/fetch/schema-coercer.d.ts +0 -10
- package/dist/src/adapters/node/index.d.ts +0 -5
- package/dist/src/adapters/node/openapi-handler-server.d.ts +0 -7
- package/dist/src/adapters/node/openapi-handler-serverless.d.ts +0 -7
- package/dist/src/adapters/node/openapi-handler.d.ts +0 -12
- package/dist/src/adapters/node/types.d.ts +0 -2
- package/dist/src/index.d.ts +0 -12
- package/dist/src/json-serializer.d.ts +0 -5
- package/dist/src/openapi-content-builder.d.ts +0 -10
- package/dist/src/openapi-error.d.ts +0 -3
- package/dist/src/openapi-generator.d.ts +0 -60
- package/dist/src/openapi-input-structure-parser.d.ts +0 -22
- package/dist/src/openapi-output-structure-parser.d.ts +0 -18
- package/dist/src/openapi-parameters-builder.d.ts +0 -12
- package/dist/src/openapi-path-parser.d.ts +0 -8
- package/dist/src/openapi.d.ts +0 -3
- package/dist/src/schema-converter.d.ts +0 -16
- package/dist/src/schema-utils.d.ts +0 -11
- package/dist/src/schema.d.ts +0 -12
- package/dist/src/utils.d.ts +0 -18
|
@@ -1,16 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from
|
|
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
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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,26 +78,20 @@ var OpenAPIContentBuilder = class {
|
|
|
33
78
|
}
|
|
34
79
|
return content;
|
|
35
80
|
}
|
|
36
|
-
}
|
|
81
|
+
}
|
|
37
82
|
|
|
38
|
-
|
|
39
|
-
|
|
83
|
+
class OpenAPIError extends Error {
|
|
84
|
+
}
|
|
40
85
|
|
|
41
|
-
|
|
42
|
-
var OpenAPIError = class extends Error {
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// src/openapi-input-structure-parser.ts
|
|
46
|
-
import { fallbackToGlobalConfig } from "@orpc/contract";
|
|
47
|
-
var OpenAPIInputStructureParser = class {
|
|
86
|
+
class OpenAPIInputStructureParser {
|
|
48
87
|
constructor(schemaConverter, schemaUtils, pathParser) {
|
|
49
88
|
this.schemaConverter = schemaConverter;
|
|
50
89
|
this.schemaUtils = schemaUtils;
|
|
51
90
|
this.pathParser = pathParser;
|
|
52
91
|
}
|
|
53
92
|
parse(contract, structure) {
|
|
54
|
-
const inputSchema = this.schemaConverter.convert(contract["~orpc"].
|
|
55
|
-
const method =
|
|
93
|
+
const inputSchema = this.schemaConverter.convert(contract["~orpc"].inputSchema, { strategy: "input" });
|
|
94
|
+
const method = fallbackContractConfig("defaultMethod", contract["~orpc"].route?.method);
|
|
56
95
|
const httpPath = contract["~orpc"].route?.path;
|
|
57
96
|
if (this.schemaUtils.isAnySchema(inputSchema)) {
|
|
58
97
|
return {
|
|
@@ -135,16 +174,15 @@ var OpenAPIInputStructureParser = class {
|
|
|
135
174
|
bodySchema: method !== "GET" ? rest : void 0
|
|
136
175
|
};
|
|
137
176
|
}
|
|
138
|
-
}
|
|
177
|
+
}
|
|
139
178
|
|
|
140
|
-
|
|
141
|
-
var OpenAPIOutputStructureParser = class {
|
|
179
|
+
class OpenAPIOutputStructureParser {
|
|
142
180
|
constructor(schemaConverter, schemaUtils) {
|
|
143
181
|
this.schemaConverter = schemaConverter;
|
|
144
182
|
this.schemaUtils = schemaUtils;
|
|
145
183
|
}
|
|
146
184
|
parse(contract, structure) {
|
|
147
|
-
const outputSchema = this.schemaConverter.convert(contract["~orpc"].
|
|
185
|
+
const outputSchema = this.schemaConverter.convert(contract["~orpc"].outputSchema, { strategy: "output" });
|
|
148
186
|
if (this.schemaUtils.isAnySchema(outputSchema)) {
|
|
149
187
|
return {
|
|
150
188
|
headersSchema: void 0,
|
|
@@ -180,17 +218,15 @@ var OpenAPIOutputStructureParser = class {
|
|
|
180
218
|
bodySchema: outputSchema
|
|
181
219
|
};
|
|
182
220
|
}
|
|
183
|
-
}
|
|
221
|
+
}
|
|
184
222
|
|
|
185
|
-
|
|
186
|
-
import { get, isPlainObject, omit } from "@orpc/shared";
|
|
187
|
-
var OpenAPIParametersBuilder = class {
|
|
223
|
+
class OpenAPIParametersBuilder {
|
|
188
224
|
build(paramIn, jsonSchema, options) {
|
|
189
225
|
const parameters = [];
|
|
190
226
|
for (const name in jsonSchema.properties) {
|
|
191
227
|
const schema = jsonSchema.properties[name];
|
|
192
228
|
const paramExamples = jsonSchema.examples?.filter((example) => {
|
|
193
|
-
return
|
|
229
|
+
return isObject(example) && name in example;
|
|
194
230
|
}).map((example) => {
|
|
195
231
|
return example[name];
|
|
196
232
|
});
|
|
@@ -218,10 +254,9 @@ var OpenAPIParametersBuilder = class {
|
|
|
218
254
|
}
|
|
219
255
|
return headersObject;
|
|
220
256
|
}
|
|
221
|
-
}
|
|
257
|
+
}
|
|
222
258
|
|
|
223
|
-
|
|
224
|
-
var OpenAPIPathParser = class {
|
|
259
|
+
class OpenAPIPathParser {
|
|
225
260
|
parseDynamicParams(path) {
|
|
226
261
|
const raws = path.match(/\{([^}]+)\}/g) ?? [];
|
|
227
262
|
return raws.map((raw) => {
|
|
@@ -229,10 +264,9 @@ var OpenAPIPathParser = class {
|
|
|
229
264
|
return { name, raw };
|
|
230
265
|
});
|
|
231
266
|
}
|
|
232
|
-
}
|
|
267
|
+
}
|
|
233
268
|
|
|
234
|
-
|
|
235
|
-
var CompositeSchemaConverter = class {
|
|
269
|
+
class CompositeSchemaConverter {
|
|
236
270
|
converters;
|
|
237
271
|
constructor(converters) {
|
|
238
272
|
this.converters = converters;
|
|
@@ -248,15 +282,9 @@ var CompositeSchemaConverter = class {
|
|
|
248
282
|
}
|
|
249
283
|
return {};
|
|
250
284
|
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// src/schema-utils.ts
|
|
254
|
-
import { isPlainObject as isPlainObject2 } from "@orpc/shared";
|
|
285
|
+
}
|
|
255
286
|
|
|
256
|
-
|
|
257
|
-
import * as JSONSchema from "json-schema-typed/draft-2020-12";
|
|
258
|
-
import { Format } from "json-schema-typed/draft-2020-12";
|
|
259
|
-
var NON_LOGIC_KEYWORDS = [
|
|
287
|
+
const NON_LOGIC_KEYWORDS = [
|
|
260
288
|
// Core Documentation Keywords
|
|
261
289
|
"$anchor",
|
|
262
290
|
"$comment",
|
|
@@ -284,23 +312,22 @@ var NON_LOGIC_KEYWORDS = [
|
|
|
284
312
|
"$dynamicRef"
|
|
285
313
|
];
|
|
286
314
|
|
|
287
|
-
|
|
288
|
-
var SchemaUtils = class {
|
|
315
|
+
class SchemaUtils {
|
|
289
316
|
isFileSchema(schema) {
|
|
290
|
-
return
|
|
317
|
+
return isObject(schema) && schema.type === "string" && typeof schema.contentMediaType === "string";
|
|
291
318
|
}
|
|
292
319
|
isObjectSchema(schema) {
|
|
293
|
-
return
|
|
320
|
+
return isObject(schema) && schema.type === "object";
|
|
294
321
|
}
|
|
295
322
|
isAnySchema(schema) {
|
|
296
|
-
return schema === true || Object.keys(schema).length === 0;
|
|
323
|
+
return schema === true || Object.keys(schema).filter((key) => !NON_LOGIC_KEYWORDS.includes(key)).length === 0;
|
|
297
324
|
}
|
|
298
325
|
isUndefinableSchema(schema) {
|
|
299
326
|
const [matches] = this.filterSchemaBranches(schema, (schema2) => {
|
|
300
327
|
if (typeof schema2 === "boolean") {
|
|
301
328
|
return schema2;
|
|
302
329
|
}
|
|
303
|
-
return Object.keys(schema2).length === 0;
|
|
330
|
+
return Object.keys(schema2).filter((key) => !NON_LOGIC_KEYWORDS.includes(key)).length === 0;
|
|
304
331
|
});
|
|
305
332
|
return matches.length > 0;
|
|
306
333
|
}
|
|
@@ -313,7 +340,7 @@ var SchemaUtils = class {
|
|
|
313
340
|
}, {});
|
|
314
341
|
matched.required = schema.required?.filter((key) => separatedProperties.includes(key));
|
|
315
342
|
matched.examples = schema.examples?.map((example) => {
|
|
316
|
-
if (!
|
|
343
|
+
if (!isObject(example)) {
|
|
317
344
|
return example;
|
|
318
345
|
}
|
|
319
346
|
return Object.entries(example).reduce((acc, [key, value]) => {
|
|
@@ -329,7 +356,7 @@ var SchemaUtils = class {
|
|
|
329
356
|
}, {});
|
|
330
357
|
rest.required = schema.required?.filter((key) => !separatedProperties.includes(key));
|
|
331
358
|
rest.examples = schema.examples?.map((example) => {
|
|
332
|
-
if (!
|
|
359
|
+
if (!isObject(example)) {
|
|
333
360
|
return example;
|
|
334
361
|
}
|
|
335
362
|
return Object.entries(example).reduce((acc, [key, value]) => {
|
|
@@ -369,10 +396,9 @@ var SchemaUtils = class {
|
|
|
369
396
|
}
|
|
370
397
|
return [matches, schema];
|
|
371
398
|
}
|
|
372
|
-
}
|
|
399
|
+
}
|
|
373
400
|
|
|
374
|
-
|
|
375
|
-
var OpenAPIGenerator = class {
|
|
401
|
+
class OpenAPIGenerator {
|
|
376
402
|
contentBuilder;
|
|
377
403
|
parametersBuilder;
|
|
378
404
|
schemaConverter;
|
|
@@ -384,11 +410,12 @@ var OpenAPIGenerator = class {
|
|
|
384
410
|
errorHandlerStrategy;
|
|
385
411
|
ignoreUndefinedPathProcedures;
|
|
386
412
|
considerMissingTagDefinitionAsError;
|
|
413
|
+
strictErrorResponses;
|
|
387
414
|
constructor(options) {
|
|
388
415
|
this.parametersBuilder = options?.parametersBuilder ?? new OpenAPIParametersBuilder();
|
|
389
416
|
this.schemaConverter = new CompositeSchemaConverter(options?.schemaConverters ?? []);
|
|
390
417
|
this.schemaUtils = options?.schemaUtils ?? new SchemaUtils();
|
|
391
|
-
this.jsonSerializer = options?.jsonSerializer ?? new
|
|
418
|
+
this.jsonSerializer = options?.jsonSerializer ?? new OpenAPIJsonSerializer();
|
|
392
419
|
this.contentBuilder = options?.contentBuilder ?? new OpenAPIContentBuilder(this.schemaUtils);
|
|
393
420
|
this.pathParser = new OpenAPIPathParser();
|
|
394
421
|
this.inputStructureParser = options?.inputStructureParser ?? new OpenAPIInputStructureParser(this.schemaConverter, this.schemaUtils, this.pathParser);
|
|
@@ -396,6 +423,7 @@ var OpenAPIGenerator = class {
|
|
|
396
423
|
this.errorHandlerStrategy = options?.errorHandlerStrategy ?? "throw";
|
|
397
424
|
this.ignoreUndefinedPathProcedures = options?.ignoreUndefinedPathProcedures ?? false;
|
|
398
425
|
this.considerMissingTagDefinitionAsError = options?.considerMissingTagDefinitionAsError ?? false;
|
|
426
|
+
this.strictErrorResponses = options?.strictErrorResponses ?? true;
|
|
399
427
|
}
|
|
400
428
|
async generate(router, doc) {
|
|
401
429
|
const builder = new OpenApiBuilder({
|
|
@@ -403,37 +431,186 @@ var OpenAPIGenerator = class {
|
|
|
403
431
|
openapi: "3.1.1"
|
|
404
432
|
});
|
|
405
433
|
const rootTags = doc.tags?.map((tag) => tag.name) ?? [];
|
|
406
|
-
await
|
|
434
|
+
await eachAllContractProcedure({
|
|
435
|
+
path: [],
|
|
436
|
+
router
|
|
437
|
+
}, ({ contract, path }) => {
|
|
407
438
|
try {
|
|
408
439
|
const def = contract["~orpc"];
|
|
409
440
|
if (this.ignoreUndefinedPathProcedures && def.route?.path === void 0) {
|
|
410
441
|
return;
|
|
411
442
|
}
|
|
412
|
-
const method =
|
|
413
|
-
const httpPath = def.route?.path ?
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
+
}
|
|
437
614
|
if (this.considerMissingTagDefinitionAsError && def.route?.tags) {
|
|
438
615
|
const missingTag = def.route?.tags.find((tag) => !rootTags.includes(tag));
|
|
439
616
|
if (missingTag !== void 0) {
|
|
@@ -450,12 +627,11 @@ var OpenAPIGenerator = class {
|
|
|
450
627
|
operationId: path.join("."),
|
|
451
628
|
parameters: parameters.length ? parameters : void 0,
|
|
452
629
|
requestBody,
|
|
453
|
-
responses
|
|
454
|
-
[fallbackToGlobalConfig2("defaultSuccessStatus", def.route?.successStatus)]: successResponse
|
|
455
|
-
}
|
|
630
|
+
responses
|
|
456
631
|
};
|
|
632
|
+
const extendedOperation = extendOperation(operation, contract);
|
|
457
633
|
builder.addPath(httpPath, {
|
|
458
|
-
[method.toLocaleLowerCase()]:
|
|
634
|
+
[method.toLocaleLowerCase()]: extendedOperation
|
|
459
635
|
});
|
|
460
636
|
} catch (e) {
|
|
461
637
|
if (e instanceof OpenAPIError) {
|
|
@@ -474,23 +650,12 @@ var OpenAPIGenerator = class {
|
|
|
474
650
|
}
|
|
475
651
|
}
|
|
476
652
|
});
|
|
477
|
-
return this.jsonSerializer.serialize(builder.getSpec());
|
|
653
|
+
return this.jsonSerializer.serialize(builder.getSpec())[0];
|
|
478
654
|
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const oo = {
|
|
658
|
+
spec: setOperationExtender
|
|
479
659
|
};
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
JSONSchema,
|
|
483
|
-
Format as JSONSchemaFormat,
|
|
484
|
-
JSONSerializer,
|
|
485
|
-
NON_LOGIC_KEYWORDS,
|
|
486
|
-
OpenAPIContentBuilder,
|
|
487
|
-
OpenAPIGenerator,
|
|
488
|
-
OpenAPIParametersBuilder,
|
|
489
|
-
OpenAPIPathParser,
|
|
490
|
-
OpenApiBuilder,
|
|
491
|
-
SchemaUtils,
|
|
492
|
-
forEachAllContractProcedure,
|
|
493
|
-
forEachContractProcedure,
|
|
494
|
-
standardizeHTTPPath
|
|
495
|
-
};
|
|
496
|
-
//# 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 };
|