@sapporta/rest-open-api 3.52.1 → 3.52.2

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 (42) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +76 -9
  3. package/index.cjs.d.ts +1 -0
  4. package/index.cjs.default.js +1 -0
  5. package/index.cjs.js +673 -0
  6. package/index.cjs.mjs +2 -0
  7. package/index.esm.js +668 -0
  8. package/package.json +15 -5
  9. package/src/index.d.ts +2 -0
  10. package/src/lib/contract-traversal.d.ts +14 -0
  11. package/src/lib/parsers/body.d.ts +14 -0
  12. package/src/lib/parsers/headers.d.ts +14 -0
  13. package/src/lib/parsers/path-params.d.ts +20 -0
  14. package/src/lib/parsers/query-params.d.ts +15 -0
  15. package/src/lib/parsers/test-helpers.d.ts +4 -0
  16. package/src/lib/parsers/utils.d.ts +11 -0
  17. package/src/lib/ts-rest-open-api.d.ts +46 -0
  18. package/src/lib/types.d.ts +83 -0
  19. package/src/lib/utils.d.ts +5 -0
  20. package/.babelrc +0 -10
  21. package/.eslintrc.json +0 -21
  22. package/jest.config.ts +0 -16
  23. package/project.json +0 -51
  24. package/src/index.ts +0 -6
  25. package/src/lib/contract-traversal.ts +0 -217
  26. package/src/lib/parsers/body.ts +0 -71
  27. package/src/lib/parsers/headers.ts +0 -163
  28. package/src/lib/parsers/path-params.spec.ts +0 -235
  29. package/src/lib/parsers/path-params.ts +0 -140
  30. package/src/lib/parsers/query-params.spec.ts +0 -129
  31. package/src/lib/parsers/query-params.ts +0 -89
  32. package/src/lib/parsers/test-helpers.ts +0 -26
  33. package/src/lib/parsers/utils.spec.ts +0 -117
  34. package/src/lib/parsers/utils.ts +0 -82
  35. package/src/lib/ts-rest-open-api.ts +0 -245
  36. package/src/lib/types.ts +0 -109
  37. package/src/lib/utils.ts +0 -92
  38. package/tsconfig.json +0 -22
  39. package/tsconfig.lib.json +0 -10
  40. package/tsconfig.spec.json +0 -9
  41. package/typedoc.json +0 -5
  42. /package/src/lib/parsers/{index.ts → index.d.ts} +0 -0
package/index.esm.js ADDED
@@ -0,0 +1,668 @@
1
+ import { isStandardSchema, isAppRouteOtherResponse, isAppRoute } from '@sapporta/rest-core';
2
+
3
+ const schemaToParameter = (schema, where, required, key, jsonQuery) => {
4
+ let description = undefined;
5
+ if ('description' in schema) {
6
+ description = schema['description'];
7
+ delete schema['description'];
8
+ }
9
+ let examples = undefined;
10
+ if ('mediaExamples' in schema) {
11
+ examples = schema['mediaExamples'];
12
+ delete schema['mediaExamples'];
13
+ }
14
+ const isDeepObject = 'properties' in schema;
15
+ if (jsonQuery) {
16
+ return {
17
+ name: key,
18
+ in: where,
19
+ ...(description && { description }),
20
+ ...(required && { required }),
21
+ content: {
22
+ 'application/json': {
23
+ schema,
24
+ ...(examples && { examples }),
25
+ },
26
+ },
27
+ };
28
+ }
29
+ else {
30
+ return {
31
+ name: key,
32
+ in: where,
33
+ ...(examples && { examples }),
34
+ ...(description && { description }),
35
+ ...(required && { required }),
36
+ ...(isDeepObject && !jsonQuery && { style: 'deepObject' }),
37
+ schema,
38
+ };
39
+ }
40
+ };
41
+ /**
42
+ * Convert a @type {SchemaObject} to an array of @type {ParameterObject}
43
+ *
44
+ * @param schema - Zod 4 JSON schema output
45
+ * @param where - The location of the parameters
46
+ * @param jsonQuery - Whether the schema is a JSON query
47
+ * @returns The parameters for the schema
48
+ */
49
+ const schemaObjectToParameters = (schema, where, jsonQuery = false) => {
50
+ var _a;
51
+ const parameters = [];
52
+ if (schema.type === 'object') {
53
+ const requiredSet = new Set((_a = schema.required) !== null && _a !== void 0 ? _a : []);
54
+ const properties = schema.properties;
55
+ if (!properties) {
56
+ return [];
57
+ }
58
+ for (const [key, value] of Object.entries(properties)) {
59
+ parameters.push(schemaToParameter(value, where, requiredSet.has(key), key, jsonQuery));
60
+ }
61
+ }
62
+ return parameters;
63
+ };
64
+
65
+ /**
66
+ * We build up the params from both the path and the schema.
67
+ *
68
+ * This builds up the record of name -> parameter object.
69
+ */
70
+ const getParamsFromPathOnly = (path) => {
71
+ var _a;
72
+ const params = new Map();
73
+ const paramsInPath = ((_a = path.match(/:([^/]+)/g)) === null || _a === void 0 ? void 0 : _a.map((param) => param.slice(1))) || [];
74
+ for (const param of paramsInPath) {
75
+ params.set(param, {
76
+ name: param,
77
+ in: 'path',
78
+ required: true,
79
+ schema: { type: 'string' },
80
+ });
81
+ }
82
+ return params;
83
+ };
84
+ /**
85
+ * Should return schema params as priority, and then path params as fallback
86
+ *
87
+ * @param pathParams - params inferred from the path i.e. just from the string
88
+ * @param schemaParams - params from the schema
89
+ */
90
+ const mergeParams = (pathParams, schemaParams) => {
91
+ const resultMap = new Map();
92
+ for (const [name, param] of pathParams.entries()) {
93
+ resultMap.set(name, param);
94
+ }
95
+ for (const [name, param] of schemaParams.entries()) {
96
+ resultMap.set(name, param);
97
+ }
98
+ return Array.from(resultMap.values());
99
+ };
100
+ const syncFunc$4 = ({ transformSchema, appRoute, id, concatenatedPath, }) => {
101
+ const schema = appRoute.pathParams;
102
+ const paramsMap = getParamsFromPathOnly(appRoute.path);
103
+ const transformedSchema = transformSchema({
104
+ schema,
105
+ appRoute,
106
+ id,
107
+ concatenatedPath,
108
+ type: 'path',
109
+ });
110
+ if (!transformedSchema) {
111
+ return Array.from(paramsMap.values());
112
+ }
113
+ const schemaParams = schemaObjectToParameters(transformedSchema, 'path');
114
+ const schemaParamsMap = new Map(schemaParams.map((param) => [param.name, param]));
115
+ return mergeParams(paramsMap, schemaParamsMap);
116
+ };
117
+ const asyncFunc$4 = async ({ transformSchema, appRoute, id, concatenatedPath, }) => {
118
+ const schema = appRoute.pathParams;
119
+ const paramsMap = getParamsFromPathOnly(appRoute.path);
120
+ const transformedSchema = await transformSchema({
121
+ schema,
122
+ appRoute,
123
+ id,
124
+ concatenatedPath,
125
+ type: 'path',
126
+ });
127
+ if (!transformedSchema) {
128
+ return Array.from(paramsMap.values());
129
+ }
130
+ const schemaParams = schemaObjectToParameters(transformedSchema, 'path');
131
+ const schemaParamsMap = new Map(schemaParams.map((param) => [param.name, param]));
132
+ return mergeParams(paramsMap, schemaParamsMap);
133
+ };
134
+ const getPathParameterSchema = {
135
+ sync: syncFunc$4,
136
+ async: asyncFunc$4,
137
+ };
138
+
139
+ const syncFunc$3 = ({ transformSchema, appRoute, id, concatenatedPath, }) => {
140
+ const schema = appRoute.headers;
141
+ if (schema === null) {
142
+ return [];
143
+ }
144
+ if (schema === undefined) {
145
+ return [];
146
+ }
147
+ if (typeof schema === 'symbol') {
148
+ return [];
149
+ }
150
+ if (isStandardSchema(schema)) {
151
+ const transformedSchema = transformSchema({
152
+ schema,
153
+ appRoute,
154
+ id,
155
+ concatenatedPath,
156
+ type: 'header',
157
+ });
158
+ if (!transformedSchema) {
159
+ return [];
160
+ }
161
+ return schemaObjectToParameters(transformedSchema, 'header');
162
+ }
163
+ const parameters = [];
164
+ for (const [key, subSchema] of Object.entries(schema)) {
165
+ if (isStandardSchema(subSchema)) {
166
+ const transformedSchema = transformSchema({
167
+ schema: subSchema,
168
+ appRoute,
169
+ id,
170
+ concatenatedPath,
171
+ type: 'header',
172
+ });
173
+ if (!transformedSchema) {
174
+ return [];
175
+ }
176
+ parameters.push(...schemaObjectToParameters(transformedSchema, 'header'));
177
+ }
178
+ }
179
+ return parameters;
180
+ };
181
+ const asyncFunc$3 = async ({ transformSchema, appRoute, id, concatenatedPath, }) => {
182
+ var _a;
183
+ const schema = appRoute.headers;
184
+ if (schema === null) {
185
+ return [];
186
+ }
187
+ if (schema === undefined) {
188
+ return [];
189
+ }
190
+ if (typeof schema === 'symbol') {
191
+ return [];
192
+ }
193
+ if (isStandardSchema(schema)) {
194
+ const transformedSchema = await transformSchema({
195
+ schema,
196
+ appRoute,
197
+ id,
198
+ concatenatedPath,
199
+ type: 'header',
200
+ });
201
+ if (!transformedSchema) {
202
+ return [];
203
+ }
204
+ return schemaObjectToParameters(transformedSchema, 'header');
205
+ }
206
+ const parameters = [];
207
+ for (const [key, subSchema] of Object.entries(schema)) {
208
+ if (isStandardSchema(subSchema)) {
209
+ const transformedSchema = await transformSchema({
210
+ schema: subSchema,
211
+ appRoute,
212
+ id,
213
+ concatenatedPath,
214
+ type: 'header',
215
+ });
216
+ if (!transformedSchema) {
217
+ continue;
218
+ }
219
+ const validateEmptyResult = subSchema['~standard'].validate(undefined);
220
+ if (validateEmptyResult instanceof Promise) {
221
+ throw new Error('Schema validation must be synchronous');
222
+ }
223
+ const isRequired = Boolean((_a = validateEmptyResult.issues) === null || _a === void 0 ? void 0 : _a.length);
224
+ const asParameter = schemaToParameter(transformedSchema, 'header', isRequired, key, false);
225
+ parameters.push(asParameter);
226
+ }
227
+ }
228
+ return parameters;
229
+ };
230
+ const getHeaderParameterSchema = {
231
+ sync: syncFunc$3,
232
+ async: asyncFunc$3,
233
+ };
234
+
235
+ const syncFunc$2 = ({ transformSchema, appRoute, id, concatenatedPath, jsonQuery = false, }) => {
236
+ const schema = appRoute.query;
237
+ const isSchema = isStandardSchema(schema);
238
+ if (!isSchema) {
239
+ return [];
240
+ }
241
+ const transformedSchema = transformSchema({
242
+ schema,
243
+ appRoute,
244
+ id,
245
+ concatenatedPath,
246
+ type: 'query',
247
+ });
248
+ if (!transformedSchema) {
249
+ return [];
250
+ }
251
+ return schemaObjectToParameters(transformedSchema, 'query', jsonQuery);
252
+ };
253
+ const asyncFunc$2 = async ({ transformSchema, appRoute, id, concatenatedPath, jsonQuery = false, }) => {
254
+ const schema = appRoute.query;
255
+ const isSchema = isStandardSchema(schema);
256
+ if (!isSchema) {
257
+ return [];
258
+ }
259
+ const transformedSchema = await transformSchema({
260
+ schema,
261
+ appRoute,
262
+ id,
263
+ concatenatedPath,
264
+ type: 'query',
265
+ });
266
+ if (!transformedSchema) {
267
+ return [];
268
+ }
269
+ return schemaObjectToParameters(transformedSchema, 'query', jsonQuery);
270
+ };
271
+ const getQueryParameterSchema = {
272
+ sync: syncFunc$2,
273
+ async: asyncFunc$2,
274
+ };
275
+
276
+ const syncFunc$1 = ({ transformSchema, appRoute, id, concatenatedPath, }) => {
277
+ const schema = 'body' in appRoute ? appRoute.body : undefined;
278
+ const transformedSchema = transformSchema({
279
+ schema,
280
+ appRoute,
281
+ id,
282
+ concatenatedPath,
283
+ type: 'body',
284
+ });
285
+ if (!transformedSchema) {
286
+ return null;
287
+ }
288
+ return transformedSchema;
289
+ };
290
+ const asyncFunc$1 = async ({ transformSchema, appRoute, id, concatenatedPath, }) => {
291
+ const schema = 'body' in appRoute ? appRoute.body : undefined;
292
+ const transformedSchema = await transformSchema({
293
+ schema,
294
+ appRoute,
295
+ id,
296
+ concatenatedPath,
297
+ type: 'body',
298
+ });
299
+ return transformedSchema;
300
+ };
301
+ const getBodySchema = {
302
+ sync: syncFunc$1,
303
+ async: asyncFunc$1,
304
+ };
305
+
306
+ /**
307
+ * Recursively step through the router and get all the individual routes with their paths etc.
308
+ */
309
+ const getPathsFromRouter = (router, pathHistory) => {
310
+ const paths = [];
311
+ Object.keys(router).forEach((key) => {
312
+ const value = router[key];
313
+ if (isAppRoute(value)) {
314
+ const pathWithPathParams = value.path.replace(/:(\w+)/g, '{$1}');
315
+ paths.push({
316
+ id: key,
317
+ path: pathWithPathParams,
318
+ route: value,
319
+ paths: pathHistory !== null && pathHistory !== void 0 ? pathHistory : [],
320
+ });
321
+ }
322
+ else {
323
+ paths.push(...getPathsFromRouter(value, [...(pathHistory !== null && pathHistory !== void 0 ? pathHistory : []), key]));
324
+ }
325
+ });
326
+ return paths;
327
+ };
328
+ const syncFunc = ({ contract, transformSchema, jsonQuery, }) => {
329
+ const paths = getPathsFromRouter(contract);
330
+ const results = [];
331
+ for (const path of paths) {
332
+ const concatenatedPath = [...path.paths, path.id].join('.');
333
+ const pathParams = getPathParameterSchema.sync({
334
+ transformSchema,
335
+ appRoute: path.route,
336
+ id: path.id,
337
+ concatenatedPath,
338
+ });
339
+ const headerParams = getHeaderParameterSchema.sync({
340
+ transformSchema,
341
+ appRoute: path.route,
342
+ id: path.id,
343
+ concatenatedPath,
344
+ });
345
+ const querySchema = getQueryParameterSchema.sync({
346
+ transformSchema,
347
+ appRoute: path.route,
348
+ id: path.id,
349
+ concatenatedPath,
350
+ jsonQuery: !!jsonQuery,
351
+ });
352
+ const bodySchema = getBodySchema.sync({
353
+ transformSchema,
354
+ appRoute: path.route,
355
+ id: path.id,
356
+ concatenatedPath,
357
+ });
358
+ const responses = {};
359
+ for (const [statusCode, _response] of Object.entries(path.route.responses)) {
360
+ const schemaValidator = isAppRouteOtherResponse(_response)
361
+ ? _response.body
362
+ : _response;
363
+ const responseSchema = transformSchema({
364
+ schema: schemaValidator,
365
+ appRoute: path.route,
366
+ id: path.id,
367
+ concatenatedPath,
368
+ type: 'response',
369
+ });
370
+ if (responseSchema) {
371
+ responses[statusCode] = responseSchema;
372
+ }
373
+ }
374
+ results.push({
375
+ ...path,
376
+ schemaResults: {
377
+ path: pathParams,
378
+ headers: headerParams,
379
+ query: querySchema,
380
+ body: bodySchema,
381
+ responses,
382
+ },
383
+ });
384
+ }
385
+ return results;
386
+ };
387
+ const asyncFunc = async ({ contract, transformSchema, jsonQuery, }) => {
388
+ const paths = getPathsFromRouter(contract);
389
+ const results = [];
390
+ for (const path of paths) {
391
+ const concatenatedPath = [...path.paths, path.id].join('.');
392
+ const pathParams = await getPathParameterSchema.async({
393
+ transformSchema,
394
+ appRoute: path.route,
395
+ id: path.id,
396
+ concatenatedPath,
397
+ });
398
+ const headerParams = await getHeaderParameterSchema.async({
399
+ transformSchema,
400
+ appRoute: path.route,
401
+ id: path.id,
402
+ concatenatedPath,
403
+ });
404
+ const querySchema = await getQueryParameterSchema.async({
405
+ transformSchema,
406
+ appRoute: path.route,
407
+ id: path.id,
408
+ concatenatedPath,
409
+ jsonQuery: !!jsonQuery,
410
+ });
411
+ const bodySchema = await getBodySchema.async({
412
+ transformSchema,
413
+ appRoute: path.route,
414
+ id: path.id,
415
+ concatenatedPath,
416
+ });
417
+ const responses = {};
418
+ for (const [statusCode, _response] of Object.entries(path.route.responses)) {
419
+ const schemaValidator = isAppRouteOtherResponse(_response)
420
+ ? _response.body
421
+ : _response;
422
+ const responseSchema = await transformSchema({
423
+ schema: schemaValidator,
424
+ appRoute: path.route,
425
+ id: path.id,
426
+ concatenatedPath,
427
+ type: 'response',
428
+ });
429
+ if (responseSchema) {
430
+ responses[statusCode] = responseSchema;
431
+ }
432
+ }
433
+ results.push({
434
+ ...path,
435
+ schemaResults: {
436
+ path: pathParams,
437
+ headers: headerParams,
438
+ query: querySchema,
439
+ body: bodySchema,
440
+ responses,
441
+ },
442
+ });
443
+ }
444
+ return results;
445
+ };
446
+ const performContractTraversal = {
447
+ sync: syncFunc,
448
+ async: asyncFunc,
449
+ };
450
+
451
+ const convertSchemaObjectToMediaTypeObject = (input) => {
452
+ const { mediaExamples: examples, ...schema } = input;
453
+ return {
454
+ schema,
455
+ ...(examples && { examples }),
456
+ };
457
+ };
458
+ const extractReferenceSchemas = (schema, referenceSchemas) => {
459
+ var _a, _b, _c;
460
+ if (schema.allOf) {
461
+ schema.allOf = (_a = schema.allOf) === null || _a === void 0 ? void 0 : _a.map((subSchema) => extractReferenceSchemas(subSchema, referenceSchemas));
462
+ }
463
+ if (schema.anyOf) {
464
+ schema.anyOf = (_b = schema.anyOf) === null || _b === void 0 ? void 0 : _b.map((subSchema) => extractReferenceSchemas(subSchema, referenceSchemas));
465
+ }
466
+ if (schema.oneOf) {
467
+ schema.oneOf = (_c = schema.oneOf) === null || _c === void 0 ? void 0 : _c.map((subSchema) => extractReferenceSchemas(subSchema, referenceSchemas));
468
+ }
469
+ if (schema.not) {
470
+ schema.not = extractReferenceSchemas(schema.not, referenceSchemas);
471
+ }
472
+ if (schema.items) {
473
+ schema.items = extractReferenceSchemas(schema.items, referenceSchemas);
474
+ }
475
+ if (schema.properties) {
476
+ schema.properties = Object.entries(schema.properties).reduce((prev, [propertyName, schema]) => {
477
+ prev[propertyName] = extractReferenceSchemas(schema, referenceSchemas);
478
+ return prev;
479
+ }, {});
480
+ }
481
+ if (schema.additionalProperties) {
482
+ schema.additionalProperties =
483
+ typeof schema.additionalProperties != 'boolean'
484
+ ? extractReferenceSchemas(schema.additionalProperties, referenceSchemas)
485
+ : schema.additionalProperties;
486
+ }
487
+ if (schema.title) {
488
+ const nullable = schema.nullable;
489
+ schema.nullable = undefined;
490
+ if (schema.title in referenceSchemas) {
491
+ if (JSON.stringify(referenceSchemas[schema.title]) !==
492
+ JSON.stringify(schema)) {
493
+ throw new Error(`Schema title '${schema.title}' already exists with a different schema`);
494
+ }
495
+ }
496
+ else {
497
+ referenceSchemas[schema.title] = schema;
498
+ }
499
+ if (nullable) {
500
+ schema = {
501
+ nullable: true,
502
+ allOf: [
503
+ {
504
+ $ref: `#/components/schemas/${schema.title}`,
505
+ },
506
+ ],
507
+ };
508
+ }
509
+ else {
510
+ schema = {
511
+ $ref: `#/components/schemas/${schema.title}`,
512
+ };
513
+ }
514
+ }
515
+ return schema;
516
+ };
517
+
518
+ /**
519
+ * Generate OpenAPI specification from ts-rest router
520
+ *
521
+ * @param router - The ts-rest router to generate OpenAPI from
522
+ * @param apiDoc - Base OpenAPI document configuration
523
+ * @param options - Generation options
524
+ * @param options.setOperationId - Whether to set operation IDs (true, false, or 'concatenated-path')
525
+ * @param options.jsonQuery - Enable JSON query parameters, [see](/docs/open-api#json-query-params)
526
+ * @param options.operationMapper - Function to customize OpenAPI operations. Receives the operation object, app route, and operation ID
527
+ * @returns OpenAPI specification object
528
+ */
529
+ function generateOpenApi(router, apiDoc, options) {
530
+ const paths = performContractTraversal.sync({
531
+ contract: router,
532
+ transformSchema: options === null || options === void 0 ? void 0 : options.schemaTransformer,
533
+ jsonQuery: !!(options === null || options === void 0 ? void 0 : options.jsonQuery),
534
+ });
535
+ return traversedPathsToOpenApi(paths, apiDoc, {
536
+ setOperationId: options === null || options === void 0 ? void 0 : options.setOperationId,
537
+ jsonQuery: options === null || options === void 0 ? void 0 : options.jsonQuery,
538
+ operationMapper: options === null || options === void 0 ? void 0 : options.operationMapper,
539
+ });
540
+ }
541
+ /**
542
+ * Generate OpenAPI specification from ts-rest router with custom schema transformer
543
+ *
544
+ * @param router - The ts-rest router to generate OpenAPI from
545
+ * @param apiDoc - Base OpenAPI document configuration
546
+ * @param options - Generation options
547
+ * @param options.setOperationId - Whether to set operation IDs (true, false, or 'concatenated-path')
548
+ * @param options.jsonQuery - Enable JSON query parameters, [see](/docs/open-api#json-query-params)
549
+ * @param options.operationMapper - Function to customize OpenAPI operations. Receives the operation object, app route, and operation ID
550
+ * @param options.schemaTransformer - Custom schema transformer function.
551
+ */
552
+ async function generateOpenApiAsync(router, apiDoc, options) {
553
+ const paths = await performContractTraversal.async({
554
+ contract: router,
555
+ transformSchema: options.schemaTransformer,
556
+ jsonQuery: !!options.jsonQuery,
557
+ });
558
+ return traversedPathsToOpenApi(paths, apiDoc, {
559
+ setOperationId: options.setOperationId,
560
+ jsonQuery: options.jsonQuery,
561
+ operationMapper: options.operationMapper,
562
+ });
563
+ }
564
+ /**
565
+ * Inner function to be reused by both sync and async functions
566
+ */
567
+ const traversedPathsToOpenApi = (paths, apiDoc, options) => {
568
+ var _a, _b, _c;
569
+ const mapMethod = {
570
+ GET: 'get',
571
+ POST: 'post',
572
+ PUT: 'put',
573
+ DELETE: 'delete',
574
+ PATCH: 'patch',
575
+ };
576
+ const operationIds = new Map();
577
+ const referenceSchemas = {};
578
+ const pathObject = {};
579
+ for (const path of paths) {
580
+ if (options.setOperationId === true) {
581
+ const existingOp = operationIds.get(path.id);
582
+ if (existingOp) {
583
+ throw new Error(`Route '${path.id}' already defined under ${existingOp.join('.')}`);
584
+ }
585
+ operationIds.set(path.id, path.paths);
586
+ }
587
+ const _bodySchema = path.schemaResults.body;
588
+ const bodySchema = _bodySchema && typeof _bodySchema === 'object' && 'title' in _bodySchema
589
+ ? extractReferenceSchemas(path.schemaResults.body, referenceSchemas)
590
+ : path.schemaResults.body;
591
+ const responses = {};
592
+ for (const [statusCode, response] of Object.entries(path.route.responses)) {
593
+ const contentType = isAppRouteOtherResponse(response)
594
+ ? response.contentType
595
+ : 'application/json';
596
+ const responseSchemaObject = path.schemaResults.responses[statusCode];
597
+ const responseSchemaObjectWithReferences = responseSchemaObject
598
+ ? extractReferenceSchemas(responseSchemaObject, referenceSchemas)
599
+ : null;
600
+ responses[statusCode] = {
601
+ ...(responseSchemaObjectWithReferences
602
+ ? {
603
+ content: {
604
+ [contentType]: {
605
+ ...convertSchemaObjectToMediaTypeObject(responseSchemaObjectWithReferences),
606
+ },
607
+ },
608
+ }
609
+ : {}),
610
+ };
611
+ }
612
+ const contentType = ((_a = path.route) === null || _a === void 0 ? void 0 : _a.method) !== 'GET' && 'contentType' in path.route
613
+ ? (_c = (_b = path.route) === null || _b === void 0 ? void 0 : _b.contentType) !== null && _c !== void 0 ? _c : 'application/json'
614
+ : 'application/json';
615
+ const pathOperation = {
616
+ description: path.route.description,
617
+ summary: path.route.summary,
618
+ deprecated: path.route.deprecated,
619
+ tags: path.paths,
620
+ parameters: [
621
+ ...path.schemaResults.path,
622
+ ...path.schemaResults.headers,
623
+ ...path.schemaResults.query,
624
+ ],
625
+ ...(options.setOperationId
626
+ ? {
627
+ operationId: options.setOperationId === 'concatenated-path'
628
+ ? [...path.paths, path.id].join('.')
629
+ : path.id,
630
+ }
631
+ : {}),
632
+ ...(bodySchema
633
+ ? {
634
+ requestBody: {
635
+ description: 'Body',
636
+ content: {
637
+ [contentType]: {
638
+ ...convertSchemaObjectToMediaTypeObject(bodySchema),
639
+ },
640
+ },
641
+ },
642
+ }
643
+ : {}),
644
+ responses,
645
+ };
646
+ pathObject[path.path] = {
647
+ ...pathObject[path.path],
648
+ [mapMethod[path.route.method]]: options.operationMapper
649
+ ? options.operationMapper(pathOperation, path.route, path.id)
650
+ : pathOperation,
651
+ };
652
+ }
653
+ if (Object.keys(referenceSchemas).length) {
654
+ apiDoc['components'] = {
655
+ schemas: {
656
+ ...referenceSchemas,
657
+ },
658
+ ...apiDoc['components'],
659
+ };
660
+ }
661
+ return {
662
+ openapi: '3.0.2',
663
+ paths: pathObject,
664
+ ...apiDoc,
665
+ };
666
+ };
667
+
668
+ export { generateOpenApi, generateOpenApiAsync };
package/package.json CHANGED
@@ -1,21 +1,31 @@
1
1
  {
2
2
  "name": "@sapporta/rest-open-api",
3
- "version": "3.52.1",
3
+ "version": "3.52.2",
4
4
  "description": "Sapporta-maintained ts-rest OpenAPI generator for Zod 4 contracts",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "https://github.com/sapporta/sapporta-rest.git",
7
+ "url": "https://github.com/jasim/sapporta-rest.git",
8
8
  "directory": "libs/ts-rest/open-api"
9
9
  },
10
10
  "license": "MIT",
11
+ "exports": {
12
+ "./package.json": "./package.json",
13
+ ".": {
14
+ "module": "./index.esm.js",
15
+ "import": "./index.cjs.mjs",
16
+ "default": "./index.cjs.js"
17
+ }
18
+ },
19
+ "main": "./index.cjs.js",
20
+ "module": "./index.esm.js",
11
21
  "dependencies": {
12
- "@sapporta/rest-core": "workspace:*",
13
22
  "openapi3-ts": "^2.0.2"
14
23
  },
15
- "peerDependencies": {
24
+ "devDependencies": {
16
25
  "zod": "^4.0.0"
17
26
  },
18
- "devDependencies": {
27
+ "peerDependencies": {
28
+ "@sapporta/rest-core": "~3.52.0",
19
29
  "zod": "^4.0.0"
20
30
  }
21
31
  }
package/src/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './lib/ts-rest-open-api';
2
+ export { SchemaTransformer, SchemaTransformerAsync, SchemaTransformerSync, } from './lib/types';