@bepalo/spine 1.4.13 → 1.5.14
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/dist/cjs/generator.d.ts.map +1 -1
- package/dist/cjs/generator.js +39 -3
- package/dist/cjs/generator.js.map +1 -1
- package/dist/cjs/router.d.ts +91 -32
- package/dist/cjs/router.d.ts.map +1 -1
- package/dist/cjs/router.js +396 -92
- package/dist/cjs/router.js.map +1 -1
- package/dist/cjs/types.d.ts +4 -99
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/types.js +15 -0
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/types.openapi.d.ts +238 -0
- package/dist/cjs/types.openapi.d.ts.map +1 -0
- package/dist/cjs/types.openapi.js +15 -0
- package/dist/cjs/types.openapi.js.map +1 -0
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +39 -3
- package/dist/generator.js.map +1 -1
- package/dist/router.d.ts +91 -32
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +396 -92
- package/dist/router.js.map +1 -1
- package/dist/types.d.ts +4 -99
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -1
- package/dist/types.openapi.d.ts +238 -0
- package/dist/types.openapi.d.ts.map +1 -0
- package/dist/types.openapi.js +15 -0
- package/dist/types.openapi.js.map +1 -0
- package/package.json +1 -1
package/dist/cjs/router.js
CHANGED
|
@@ -26,7 +26,7 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
|
26
26
|
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
27
27
|
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
28
28
|
};
|
|
29
|
-
var _Router_instances, _Router_config, _Router_routes, _Router_processPath, _Router_getRouteEntries, _Router_InitEntries, _Router_initRoutes;
|
|
29
|
+
var _Router_instances, _Router_config, _Router_routes, _Router_generateUniqueOperationId, _Router_processPath, _Router_getRouteEntries, _Router_InitEntries, _Router_initRoutes;
|
|
30
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
31
|
exports.translateRouteFilePath = exports.Router = exports.HANDLER_TYPES = exports.HTTP_METHODS_UPPER = exports.CRUD_METHODS = exports.HTTP_METHODS = exports.REGISTER_PATH_REGEX = exports.PATH_PART_REGEX = void 0;
|
|
32
32
|
const status_ts_1 = require("./status.js");
|
|
@@ -465,19 +465,172 @@ class Router {
|
|
|
465
465
|
}
|
|
466
466
|
});
|
|
467
467
|
}
|
|
468
|
-
|
|
469
|
-
|
|
468
|
+
/**
|
|
469
|
+
* Generates an OpenAPI 3.0.0 specification document from the registered routes.
|
|
470
|
+
*
|
|
471
|
+
* This method scans all registered routes and builds a comprehensive OpenAPI specification
|
|
472
|
+
* that can be used with Swagger UI, Redoc, or any OpenAPI-compatible tooling.
|
|
473
|
+
*
|
|
474
|
+
* @param info - Configuration for the OpenAPI document
|
|
475
|
+
* @param info.title - API title (defaults to "API")
|
|
476
|
+
* @param info.version - API version (defaults to "1.0.0")
|
|
477
|
+
* @param info.description - API description
|
|
478
|
+
* @param info.servers - Array of server URLs and descriptions
|
|
479
|
+
* @param info.security - Global security requirements
|
|
480
|
+
* @param info.components - Reusable components (schemas, securitySchemes, parameters, responses, examples)
|
|
481
|
+
* @param info.termsOfService - URL to terms of service
|
|
482
|
+
* @param info.contact - Contact information for the API
|
|
483
|
+
* @param info.license - License information
|
|
484
|
+
* @param info.tags - Global tags for grouping operations
|
|
485
|
+
* @param info.externalDocs - External documentation reference
|
|
486
|
+
*
|
|
487
|
+
* @param options - Generation options
|
|
488
|
+
* @param {Map<HttpMethodUpper,number>} options.sortMethodPriorityMap - Map method to priority for the default sorter.
|
|
489
|
+
* @param {1|-1|undefined} options.sortPathnameOrder - Enable and define the pathname sort order for the default sorter.
|
|
490
|
+
* @param {1|-1|undefined} options.sortMethodOrder - Enable and define the method sort order for the default sorter.
|
|
491
|
+
* @param {1|-1|undefined} options.sortTagsOrder - Enable and define the tags sort order for the default sorter.
|
|
492
|
+
* @param options.pick - Filter function to selectively include routes
|
|
493
|
+
* - Receives one `GenerateOpenAPISortParam` parameter: `{ method, path, parts, tags }`
|
|
494
|
+
* - Return true to include, false to exclude
|
|
495
|
+
* @param options.routeSorter - Custom route sorter.
|
|
496
|
+
* - Recieves two `GenerateOpenAPISortParam` parameters: `{ method, path, parts, tags }`
|
|
497
|
+
* - Return -1, 0, 1
|
|
498
|
+
* @param options.includeOperationId - Whether to generate operationId for each operation (default: true)
|
|
499
|
+
* @param options.autoTag - Automatically tag operations based on path (default: true)
|
|
500
|
+
* - When `true`, uses the first path segment as the tag
|
|
501
|
+
* - When a function, allows custom tag generation
|
|
502
|
+
* @param options.commonParameters - Parameters to apply to all operations
|
|
503
|
+
* @param options.autoSummary - Generate summary from path when not provided (default: true)
|
|
504
|
+
* @param options.cleanOperationId - Remove special characters from operation IDs (default: true)
|
|
505
|
+
|
|
506
|
+
* @returns {Promise<GeneratedOpenApi>} A promise that resolves to the complete OpenAPI 3.0.0 specification
|
|
507
|
+
*
|
|
508
|
+
* @example
|
|
509
|
+
* ```typescript
|
|
510
|
+
* // Basic usage
|
|
511
|
+
* const openapi = await router.generateOpenAPI({
|
|
512
|
+
* title: "My API",
|
|
513
|
+
* version: "2.0.0",
|
|
514
|
+
* description: "My awesome API",
|
|
515
|
+
* });
|
|
516
|
+
*
|
|
517
|
+
* // With custom options
|
|
518
|
+
* const openapi = await router.generateOpenAPI(
|
|
519
|
+
* { title: "My API", version: "1.0.0" },
|
|
520
|
+
* {
|
|
521
|
+
* pick: ({ tags, path, parts, method }) => {
|
|
522
|
+
* return path.startsWith('/api');
|
|
523
|
+
* },
|
|
524
|
+
* autoTag: ({ path }) => {
|
|
525
|
+
* const parts = path.split('/').filter(p => p && p !== "*");
|
|
526
|
+
* return parts.length > 0 ? [parts[0]] : ['default'];
|
|
527
|
+
* },
|
|
528
|
+
* commonParameters: [
|
|
529
|
+
* {
|
|
530
|
+
* name: 'X-Request-ID',
|
|
531
|
+
* in: 'header',
|
|
532
|
+
* schema: { type: 'string' },
|
|
533
|
+
* description: 'Request ID for tracing'
|
|
534
|
+
* }
|
|
535
|
+
* ]
|
|
536
|
+
* }
|
|
537
|
+
* );
|
|
538
|
+
*
|
|
539
|
+
* // Write to file
|
|
540
|
+
* await Deno.writeTextFile('./openapi.json', JSON.stringify(openapi, null, 2));
|
|
541
|
+
* ```
|
|
542
|
+
*
|
|
543
|
+
* @remarks
|
|
544
|
+
* - Routes with `openApi: false` in their definition are excluded from generation
|
|
545
|
+
* - Super glob routes (`/**`) are automatically excluded
|
|
546
|
+
* - Unnamed glob routes (`/*`) are named by their index like so 'glob1'
|
|
547
|
+
* - Connect HTTP method is not supported
|
|
548
|
+
* - Tags are only included if they are actually used by at least one operation
|
|
549
|
+
* - Response schemas are only included when explicitly provided
|
|
550
|
+
* - Path parameters are automatically extracted from route definitions and forced to `required: true`
|
|
551
|
+
* - Operation IDs are guaranteed to be unique with collision detection
|
|
552
|
+
*
|
|
553
|
+
* @see {@link GenerateOpenApiInfo} for complete info options
|
|
554
|
+
* @see {@link GenerateOpenAPIOptions} for complete generation options
|
|
555
|
+
* @see {@link GeneratedOpenApi} for the return type structure
|
|
556
|
+
*/
|
|
557
|
+
generateOpenAPI(info, options) {
|
|
558
|
+
var _a;
|
|
559
|
+
const { title = "API", version = "1.0.0", description, servers = [{ url: "/", description: "Current server" }], security, components: globalComponents, termsOfService, contact, license, tags: globalTags, externalDocs, } = info !== null && info !== void 0 ? info : Object.create(null);
|
|
560
|
+
const sortMethodPriorityMap = (_a = options === null || options === void 0 ? void 0 : options.sortMethodPriorityMap) !== null && _a !== void 0 ? _a : types_ts_1.SORT_METHOD_PRIORITY_INDEX;
|
|
561
|
+
const sortPathnameOrder = (options === null || options === void 0 ? void 0 : options.sortPathnameOrder) && options.sortPathnameOrder < 0 ? -1 : 1;
|
|
562
|
+
const sortMethodOrder = (options === null || options === void 0 ? void 0 : options.sortMethodOrder) && options.sortMethodOrder < 0 ? -1 : 1;
|
|
563
|
+
const sortTagsOrder = (options === null || options === void 0 ? void 0 : options.sortTagsOrder) && options.sortTagsOrder < 0 ? -1 : 1;
|
|
564
|
+
const { pick, includeOperationId = true, autoTag = true, commonParameters = [], autoSummary = true, cleanOperationId = true, routeSorter = (a, b) => {
|
|
565
|
+
// sort by tags
|
|
566
|
+
if ((options === null || options === void 0 ? void 0 : options.sortTagsOrder) != null) {
|
|
567
|
+
const minTagsLen = Math.min(a.tags.length, b.tags.length);
|
|
568
|
+
for (let i = 0; i < minTagsLen; i++) {
|
|
569
|
+
const comp = sortTagsOrder * a.tags[i].localeCompare(b.tags[i]);
|
|
570
|
+
if (comp !== 0) {
|
|
571
|
+
return comp;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (minTagsLen === 0 && a.tags.length !== b.tags.length) {
|
|
575
|
+
const comp = sortTagsOrder * (a.tags.length < b.tags.length ? -1 : 1);
|
|
576
|
+
if (comp !== 0) {
|
|
577
|
+
return comp;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
// sort by pathname parts
|
|
582
|
+
if ((options === null || options === void 0 ? void 0 : options.sortPathnameOrder) != null) {
|
|
583
|
+
const minLen = Math.min(a.parts.length, b.parts.length);
|
|
584
|
+
for (let i = 0; i < minLen; i++) {
|
|
585
|
+
let comp = sortPathnameOrder * a.parts[i].localeCompare(b.parts[i]);
|
|
586
|
+
if (comp !== 0) {
|
|
587
|
+
return comp;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (a.parts.length != b.parts.length) {
|
|
591
|
+
const comp = sortPathnameOrder * (a.parts.length < b.parts.length ? -1 : 1);
|
|
592
|
+
if (comp !== 0) {
|
|
593
|
+
return comp;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
// sort by methods
|
|
598
|
+
if ((options === null || options === void 0 ? void 0 : options.sortMethodOrder) != null) {
|
|
599
|
+
if (a.method !== b.method) {
|
|
600
|
+
const ma = sortMethodPriorityMap.get(a.method);
|
|
601
|
+
const mb = sortMethodPriorityMap.get(b.method);
|
|
602
|
+
return ma === mb
|
|
603
|
+
? sortMethodOrder * a.method.localeCompare(b.method)
|
|
604
|
+
: sortMethodOrder * (ma - mb);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return 0;
|
|
608
|
+
}, } = options !== null && options !== void 0 ? options : Object.create(null);
|
|
470
609
|
return new Promise((resolve) => {
|
|
471
|
-
var _a, _b, _c;
|
|
610
|
+
var _a, _b, _c, _d;
|
|
472
611
|
const handlers = __classPrivateFieldGet(this, _Router_routes, "f").handler;
|
|
473
612
|
const paths = {};
|
|
474
613
|
const schemas = {};
|
|
475
614
|
const securitySchemes = {};
|
|
615
|
+
const parameters = {};
|
|
616
|
+
const responses = {};
|
|
617
|
+
const examples = {};
|
|
618
|
+
const usedTags = new Set();
|
|
619
|
+
const usedOperationIds = new Set();
|
|
620
|
+
const warnings = [];
|
|
476
621
|
// Group routes by path
|
|
477
|
-
const
|
|
622
|
+
const routeGroupsSorter = [];
|
|
623
|
+
const routeGroups = Object.create(null);
|
|
478
624
|
// Collect all routes
|
|
479
625
|
for (const method of Object.keys(handlers)) {
|
|
480
|
-
const
|
|
626
|
+
const methodUpper = method.toUpperCase();
|
|
627
|
+
// Check if method is supported by OpenAPI
|
|
628
|
+
if (methodUpper === "CONNECT") {
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
const methodHandlers = handlers[methodUpper];
|
|
632
|
+
if (!methodHandlers)
|
|
633
|
+
continue;
|
|
481
634
|
for (const entries of [
|
|
482
635
|
methodHandlers.entries,
|
|
483
636
|
methodHandlers.globs,
|
|
@@ -489,96 +642,159 @@ class Router {
|
|
|
489
642
|
for (const [, entry] of bucket) {
|
|
490
643
|
if (entry == null)
|
|
491
644
|
continue;
|
|
492
|
-
//
|
|
493
|
-
if (entry.
|
|
645
|
+
// Check openApi first
|
|
646
|
+
if (entry.openApi === false)
|
|
647
|
+
continue;
|
|
648
|
+
// Skip super glob routes
|
|
649
|
+
if (entry.path.endsWith("/**"))
|
|
494
650
|
continue;
|
|
651
|
+
let tags = (_a = entry.openApi) === null || _a === void 0 ? void 0 : _a.tags;
|
|
652
|
+
// Auto-tag based on path if no explicit tags and autoTag is enabled
|
|
653
|
+
if (autoTag && !tags) {
|
|
654
|
+
const pathParts = entry.pathParts.filter((p) => p && p !== "*");
|
|
655
|
+
if (pathParts.length > 0) {
|
|
656
|
+
if (typeof autoTag === "function") {
|
|
657
|
+
tags = autoTag(entry);
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
const tag = pathParts[0] || "default";
|
|
661
|
+
tags = [tag];
|
|
662
|
+
}
|
|
663
|
+
}
|
|
495
664
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
665
|
+
if (entry.openApi == null) {
|
|
666
|
+
entry.openApi = {};
|
|
667
|
+
}
|
|
668
|
+
entry.openApi.tags = tags;
|
|
669
|
+
const sortEntry = Object.freeze({
|
|
670
|
+
method: methodUpper,
|
|
671
|
+
path: entry.path,
|
|
672
|
+
parts: Object.freeze([...entry.pathParts]),
|
|
673
|
+
tags: Object.freeze(entry.openApi && tags && tags.length > 0
|
|
674
|
+
? [...tags].sort()
|
|
675
|
+
: []),
|
|
676
|
+
});
|
|
677
|
+
// Apply pick filter
|
|
678
|
+
if (typeof pick === "function" && !pick(sortEntry)) {
|
|
679
|
+
continue;
|
|
500
680
|
}
|
|
501
|
-
|
|
681
|
+
routeGroupsSorter.push([sortEntry, entry]);
|
|
502
682
|
}
|
|
503
683
|
}
|
|
504
684
|
}
|
|
505
685
|
}
|
|
686
|
+
// Sort route groups
|
|
687
|
+
routeGroupsSorter.sort(([a], [b]) => routeSorter(a, b));
|
|
688
|
+
for (const [{ method, path, parts }, entry] of routeGroupsSorter) {
|
|
689
|
+
let pathMethods = routeGroups[entry.openApiPath];
|
|
690
|
+
if (!pathMethods) {
|
|
691
|
+
pathMethods = Object.create(null);
|
|
692
|
+
routeGroups[entry.openApiPath] = pathMethods;
|
|
693
|
+
}
|
|
694
|
+
pathMethods[method] = entry;
|
|
695
|
+
}
|
|
506
696
|
// Build paths
|
|
507
|
-
for (const
|
|
697
|
+
for (const pathname of Object.keys(routeGroups)) {
|
|
698
|
+
const methods = routeGroups[pathname];
|
|
508
699
|
const pathItem = {};
|
|
509
700
|
paths[pathname] = pathItem;
|
|
510
|
-
for (const
|
|
511
|
-
const
|
|
701
|
+
for (const method of Object.keys(methods)) {
|
|
702
|
+
const entry = methods[method];
|
|
703
|
+
const openApi = entry.openApi || {};
|
|
512
704
|
const methodLower = method.toLowerCase();
|
|
513
|
-
//
|
|
514
|
-
|
|
705
|
+
// Determine tags for this operation
|
|
706
|
+
let tags = openApi.tags;
|
|
707
|
+
// // Auto-tag based on path if no explicit tags and autoTag is enabled
|
|
708
|
+
// if (autoTag && !tags) {
|
|
709
|
+
// const pathParts = pathname.split("/").filter(Boolean);
|
|
710
|
+
// if (pathParts.length > 0) {
|
|
711
|
+
// if (typeof autoTag === "function") {
|
|
712
|
+
// tags = autoTag(entry);
|
|
713
|
+
// } else {
|
|
714
|
+
// const tag = pathParts[0] || "default";
|
|
715
|
+
// tags = [tag];
|
|
716
|
+
// }
|
|
717
|
+
// }
|
|
718
|
+
// }
|
|
719
|
+
// Track all tags used by this operation
|
|
720
|
+
if (tags) {
|
|
721
|
+
for (const tag of tags) {
|
|
722
|
+
usedTags.add(tag);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
// Auto-summary from path - use the last meaningful part
|
|
726
|
+
let summary = openApi.summary;
|
|
727
|
+
if (autoSummary && !summary) {
|
|
728
|
+
const pathParts = pathname
|
|
729
|
+
.split("/")
|
|
730
|
+
.filter((p, idx) => p && entry.pathParts[idx] !== "*");
|
|
731
|
+
// Find the last non-parameter part or use the last part
|
|
732
|
+
let resource = pathParts[pathParts.length - 1] || pathParts[0] || "root";
|
|
733
|
+
// Remove OpenAPI parameter syntax for summary
|
|
734
|
+
resource = resource.replace(/[{}]/g, "");
|
|
735
|
+
const action = method.toLowerCase();
|
|
736
|
+
summary = `${action} ${resource}`;
|
|
737
|
+
}
|
|
738
|
+
// Build parameters - router-derived path parameters MUST be required: true
|
|
739
|
+
const entryParams = [];
|
|
740
|
+
let globIdx = 0;
|
|
741
|
+
for (let i = 1; i < entry.pathParts.length; i++) {
|
|
742
|
+
const part = entry.pathParts[i];
|
|
743
|
+
if (part === "*") {
|
|
744
|
+
const foundParam = (_b = entry.params) === null || _b === void 0 ? void 0 : _b.find(([idx, paramId]) => idx === i);
|
|
745
|
+
entryParams.push([
|
|
746
|
+
i,
|
|
747
|
+
foundParam ? foundParam[1] : `glob${++globIdx}`,
|
|
748
|
+
]);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
515
751
|
const pathParams = [];
|
|
516
|
-
if (
|
|
517
|
-
for (const [idx, paramId] of
|
|
518
|
-
//
|
|
519
|
-
|
|
520
|
-
pathParams.push({
|
|
752
|
+
if (entryParams.length > 0) {
|
|
753
|
+
for (const [idx, paramId] of entryParams) {
|
|
754
|
+
// Start with the router-derived parameter
|
|
755
|
+
const baseParam = {
|
|
521
756
|
name: paramId,
|
|
522
757
|
in: "path",
|
|
523
758
|
required: true,
|
|
524
759
|
schema: { type: "string" },
|
|
525
|
-
}
|
|
760
|
+
};
|
|
761
|
+
// Check if user defined this parameter
|
|
762
|
+
const userParam = ((_c = openApi.parameters) !== null && _c !== void 0 ? _c : []).find((p) => p.name === paramId && p.in === "path");
|
|
763
|
+
if (userParam) {
|
|
764
|
+
// Merge user metadata while preserving required: true
|
|
765
|
+
pathParams.push(Object.assign(Object.assign(Object.assign({}, baseParam), userParam), { required: true, in: "path" }));
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
pathParams.push(baseParam);
|
|
769
|
+
}
|
|
526
770
|
}
|
|
527
771
|
}
|
|
528
|
-
//
|
|
529
|
-
const userParams = (
|
|
530
|
-
|
|
772
|
+
// Get user-defined parameters (non-path params)
|
|
773
|
+
const userParams = ((_d = openApi.parameters) !== null && _d !== void 0 ? _d : []).filter((p) => p.in !== "path");
|
|
774
|
+
// Combine: common parameters + user params + path params
|
|
775
|
+
// Path params come last so they take precedence for required: true
|
|
776
|
+
const allParams = [...commonParameters, ...userParams, ...pathParams];
|
|
531
777
|
// Remove duplicates (by name + in combination)
|
|
532
778
|
const paramSet = new Set();
|
|
533
|
-
const
|
|
779
|
+
const finalParams = [];
|
|
534
780
|
for (const param of allParams) {
|
|
535
781
|
const key = `${param.name}:${param.in}`;
|
|
536
782
|
if (!paramSet.has(key)) {
|
|
537
783
|
paramSet.add(key);
|
|
538
|
-
|
|
784
|
+
finalParams.push(param);
|
|
539
785
|
}
|
|
540
786
|
}
|
|
541
787
|
// Build request body
|
|
542
788
|
const requestBody = openApi.requestBody;
|
|
543
|
-
// Build responses
|
|
544
|
-
const
|
|
789
|
+
// Build responses - only use provided responses, no inference
|
|
790
|
+
const responseObj = {};
|
|
545
791
|
if (openApi.responses) {
|
|
546
|
-
Object.assign(
|
|
792
|
+
Object.assign(responseObj, openApi.responses);
|
|
547
793
|
}
|
|
548
794
|
else {
|
|
549
|
-
//
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
description: "Successful response",
|
|
553
|
-
content: {
|
|
554
|
-
"application/json": {
|
|
555
|
-
schema: {
|
|
556
|
-
type: "object",
|
|
557
|
-
properties: {
|
|
558
|
-
data: { type: "object" },
|
|
559
|
-
message: { type: "string" },
|
|
560
|
-
},
|
|
561
|
-
},
|
|
562
|
-
},
|
|
563
|
-
},
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
else if (method === "DELETE") {
|
|
567
|
-
responses["204"] = {
|
|
568
|
-
description: "Resource deleted successfully",
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
// Add common error responses
|
|
572
|
-
if ((_c = entry.params) === null || _c === void 0 ? void 0 : _c.length) {
|
|
573
|
-
responses["404"] = {
|
|
574
|
-
description: "Resource not found",
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
responses["400"] = {
|
|
578
|
-
description: "Bad request",
|
|
579
|
-
};
|
|
580
|
-
responses["500"] = {
|
|
581
|
-
description: "Internal server error",
|
|
795
|
+
// Minimal default response - just a description
|
|
796
|
+
responseObj["200"] = {
|
|
797
|
+
description: "Successful response",
|
|
582
798
|
};
|
|
583
799
|
}
|
|
584
800
|
// Build security
|
|
@@ -586,16 +802,19 @@ class Router {
|
|
|
586
802
|
if (!operationSecurity && security) {
|
|
587
803
|
operationSecurity = security;
|
|
588
804
|
}
|
|
589
|
-
// Generate
|
|
590
|
-
|
|
805
|
+
// Generate operation ID with uniqueness guarantee
|
|
806
|
+
let operationId = openApi.operationId;
|
|
807
|
+
if (!operationId && includeOperationId) {
|
|
808
|
+
operationId = __classPrivateFieldGet(this, _Router_instances, "m", _Router_generateUniqueOperationId).call(this, method, pathname, cleanOperationId, usedOperationIds);
|
|
809
|
+
}
|
|
591
810
|
// Build operation object
|
|
592
811
|
const operation = {
|
|
593
|
-
summary:
|
|
812
|
+
summary: summary,
|
|
594
813
|
description: openApi.description,
|
|
595
|
-
tags:
|
|
596
|
-
parameters:
|
|
814
|
+
tags: tags,
|
|
815
|
+
parameters: finalParams.length > 0 ? finalParams : undefined,
|
|
597
816
|
requestBody,
|
|
598
|
-
responses,
|
|
817
|
+
responses: responseObj,
|
|
599
818
|
security: operationSecurity,
|
|
600
819
|
operationId,
|
|
601
820
|
};
|
|
@@ -604,33 +823,86 @@ class Router {
|
|
|
604
823
|
pathItem[methodLower] = cleanedOperation;
|
|
605
824
|
}
|
|
606
825
|
}
|
|
607
|
-
//
|
|
826
|
+
// Build components
|
|
608
827
|
const components = {};
|
|
828
|
+
// Merge global and collected schemas
|
|
609
829
|
if (globalComponents === null || globalComponents === void 0 ? void 0 : globalComponents.schemas) {
|
|
610
830
|
Object.assign(schemas, globalComponents.schemas);
|
|
611
831
|
}
|
|
612
832
|
if (Object.keys(schemas).length > 0) {
|
|
613
833
|
components.schemas = schemas;
|
|
614
834
|
}
|
|
835
|
+
// Merge global and collected security schemes
|
|
615
836
|
if (globalComponents === null || globalComponents === void 0 ? void 0 : globalComponents.securitySchemes) {
|
|
616
837
|
Object.assign(securitySchemes, globalComponents.securitySchemes);
|
|
617
838
|
}
|
|
618
839
|
if (Object.keys(securitySchemes).length > 0) {
|
|
619
840
|
components.securitySchemes = securitySchemes;
|
|
620
841
|
}
|
|
842
|
+
// Add global parameters
|
|
843
|
+
if (globalComponents === null || globalComponents === void 0 ? void 0 : globalComponents.parameters) {
|
|
844
|
+
Object.assign(parameters, globalComponents.parameters);
|
|
845
|
+
}
|
|
846
|
+
if (Object.keys(parameters).length > 0) {
|
|
847
|
+
components.parameters = parameters;
|
|
848
|
+
}
|
|
849
|
+
// Add global responses
|
|
850
|
+
if (globalComponents === null || globalComponents === void 0 ? void 0 : globalComponents.responses) {
|
|
851
|
+
Object.assign(responses, globalComponents.responses);
|
|
852
|
+
}
|
|
853
|
+
if (Object.keys(responses).length > 0) {
|
|
854
|
+
components.responses = responses;
|
|
855
|
+
}
|
|
856
|
+
// Add global examples
|
|
857
|
+
if (globalComponents === null || globalComponents === void 0 ? void 0 : globalComponents.examples) {
|
|
858
|
+
Object.assign(examples, globalComponents.examples);
|
|
859
|
+
}
|
|
860
|
+
if (Object.keys(examples).length > 0) {
|
|
861
|
+
components.examples = examples;
|
|
862
|
+
}
|
|
863
|
+
// Build final result
|
|
621
864
|
const result = {
|
|
622
865
|
openapi: "3.0.0",
|
|
623
|
-
info: Object.assign({ title,
|
|
624
|
-
version }, (description && { description })),
|
|
866
|
+
info: Object.assign(Object.assign(Object.assign(Object.assign({ title,
|
|
867
|
+
version }, (description && { description })), (termsOfService && { termsOfService })), (contact && { contact })), (license && { license })),
|
|
625
868
|
servers,
|
|
626
869
|
paths,
|
|
627
870
|
};
|
|
871
|
+
// Add tags - ONLY include tags that are actually used by operations
|
|
872
|
+
const allTags = [];
|
|
873
|
+
// Start with global tags that are actually used
|
|
874
|
+
if (globalTags) {
|
|
875
|
+
for (const tag of globalTags) {
|
|
876
|
+
if (usedTags.has(tag.name)) {
|
|
877
|
+
allTags.push(tag);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
// Add auto-generated tags that aren't already in global tags
|
|
882
|
+
for (const tag of usedTags) {
|
|
883
|
+
if (!allTags.some((t) => t.name === tag)) {
|
|
884
|
+
allTags.push({ name: tag });
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
if (allTags.length > 0) {
|
|
888
|
+
result.tags = allTags;
|
|
889
|
+
}
|
|
890
|
+
// Add external docs
|
|
891
|
+
if (externalDocs) {
|
|
892
|
+
result.externalDocs = externalDocs;
|
|
893
|
+
}
|
|
894
|
+
// Add components
|
|
628
895
|
if (Object.keys(components).length > 0) {
|
|
629
896
|
result.components = components;
|
|
630
897
|
}
|
|
898
|
+
// Add security
|
|
631
899
|
if (security && security.length > 0) {
|
|
632
900
|
result.security = security;
|
|
633
901
|
}
|
|
902
|
+
// Log warnings if any
|
|
903
|
+
if (warnings.length > 0 && typeof console !== "undefined") {
|
|
904
|
+
console.warn("OpenAPI Generation Warnings:", warnings.join("\n "));
|
|
905
|
+
}
|
|
634
906
|
resolve(result);
|
|
635
907
|
});
|
|
636
908
|
}
|
|
@@ -1049,16 +1321,24 @@ class Router {
|
|
|
1049
1321
|
const { params, paths } = processedPaths;
|
|
1050
1322
|
const paramsMap = params ? new Map(params) : undefined;
|
|
1051
1323
|
for (const path of paths) {
|
|
1052
|
-
const
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1324
|
+
const parts = path.split("/", __classPrivateFieldGet(this, _Router_config, "f").maxPath + 1);
|
|
1325
|
+
const containsParams = params || parts.some((p) => p === "*");
|
|
1326
|
+
const standardPath = containsParams
|
|
1327
|
+
? parts
|
|
1328
|
+
.map((p, idx) => p === "*"
|
|
1329
|
+
? (paramsMap === null || paramsMap === void 0 ? void 0 : paramsMap.has(idx))
|
|
1330
|
+
? `:${paramsMap.get(idx)}`
|
|
1331
|
+
: "*"
|
|
1332
|
+
: p)
|
|
1056
1333
|
.join("/")
|
|
1057
1334
|
: path;
|
|
1058
|
-
|
|
1335
|
+
let globIdx = 0;
|
|
1336
|
+
const openApiPath = containsParams
|
|
1059
1337
|
? path
|
|
1060
1338
|
.split("/")
|
|
1061
|
-
.map((p, idx) =>
|
|
1339
|
+
.map((p, idx) => p === "*"
|
|
1340
|
+
? `{${(paramsMap === null || paramsMap === void 0 ? void 0 : paramsMap.get(idx)) || `glob${++globIdx}`}}`
|
|
1341
|
+
: p)
|
|
1062
1342
|
.join("/")
|
|
1063
1343
|
: path;
|
|
1064
1344
|
const upperMethod = method.toUpperCase();
|
|
@@ -1069,7 +1349,6 @@ class Router {
|
|
|
1069
1349
|
if (!exports.REGISTER_PATH_REGEX.test(path)) {
|
|
1070
1350
|
throw new types_ts_1.RouterError(`Invalid path for (${method} ${originalPath} -> ${path})`);
|
|
1071
1351
|
}
|
|
1072
|
-
const parts = path.split("/", __classPrivateFieldGet(this, _Router_config, "f").maxPath + 1);
|
|
1073
1352
|
if (parts.length - 1 > __classPrivateFieldGet(this, _Router_config, "f").maxPath) {
|
|
1074
1353
|
throw new types_ts_1.RouterError(`Path parts length limit exceeded ${__classPrivateFieldGet(this, _Router_config, "f").maxPath}`);
|
|
1075
1354
|
}
|
|
@@ -1098,7 +1377,8 @@ class Router {
|
|
|
1098
1377
|
if (!overwrite &&
|
|
1099
1378
|
superGlobEntries &&
|
|
1100
1379
|
superGlobEntries.has(basePath)) {
|
|
1101
|
-
|
|
1380
|
+
const superGlobEntry = superGlobEntries.get(basePath);
|
|
1381
|
+
throw new types_ts_1.RouterError(`SuperGlob route collision for (${method} ${originalPath} -> ${path} with ${superGlobEntry.originalPath} at ${superGlobEntry.pathParts.join("/")})`);
|
|
1102
1382
|
}
|
|
1103
1383
|
if (superGlobEntries == null) {
|
|
1104
1384
|
superGlobEntries = new Map();
|
|
@@ -1111,7 +1391,8 @@ class Router {
|
|
|
1111
1391
|
let globEntries = routes.globs[parts.length];
|
|
1112
1392
|
if (globEntries) {
|
|
1113
1393
|
if (!overwrite && globEntries && globEntries.has(path)) {
|
|
1114
|
-
|
|
1394
|
+
const globEntry = globEntries.get(path);
|
|
1395
|
+
throw new types_ts_1.RouterError(`Glob route collision for (${method} ${originalPath} -> ${path} with ${globEntry.originalPath} at ${globEntry.pathParts.join("/")})`);
|
|
1115
1396
|
}
|
|
1116
1397
|
// check for collision
|
|
1117
1398
|
for (const globEntry of globEntries.values()) {
|
|
@@ -1181,7 +1462,18 @@ class Router {
|
|
|
1181
1462
|
}
|
|
1182
1463
|
}
|
|
1183
1464
|
exports.Router = Router;
|
|
1184
|
-
_Router_config = new WeakMap(), _Router_routes = new WeakMap(), _Router_instances = new WeakSet(),
|
|
1465
|
+
_Router_config = new WeakMap(), _Router_routes = new WeakMap(), _Router_instances = new WeakSet(), _Router_generateUniqueOperationId = function _Router_generateUniqueOperationId(method, path, clean, usedIds) {
|
|
1466
|
+
let baseId = generateOperationId(method, path, clean);
|
|
1467
|
+
let operationId = baseId;
|
|
1468
|
+
let counter = 1;
|
|
1469
|
+
// Ensure uniqueness
|
|
1470
|
+
while (usedIds.has(operationId)) {
|
|
1471
|
+
operationId = `${baseId}${counter}`;
|
|
1472
|
+
counter++;
|
|
1473
|
+
}
|
|
1474
|
+
usedIds.add(operationId);
|
|
1475
|
+
return operationId;
|
|
1476
|
+
}, _Router_processPath = function _Router_processPath(path) {
|
|
1185
1477
|
const processedPaths = {
|
|
1186
1478
|
paths: [""],
|
|
1187
1479
|
};
|
|
@@ -1502,17 +1794,29 @@ const translateRouteFilePath = (pathname, maxPath = 64) => {
|
|
|
1502
1794
|
return parts.join("/");
|
|
1503
1795
|
};
|
|
1504
1796
|
exports.translateRouteFilePath = translateRouteFilePath;
|
|
1505
|
-
const generateOperationId = (method, path) => {
|
|
1506
|
-
//
|
|
1507
|
-
|
|
1508
|
-
.replace(/[{}]/g, "")
|
|
1509
|
-
.replace(/\*/g, "")
|
|
1510
|
-
.replace(/[^a-zA-Z0-9\/]/g, "")
|
|
1797
|
+
const generateOperationId = (method, path, clean = true) => {
|
|
1798
|
+
// Clean path
|
|
1799
|
+
let cleanPath = path
|
|
1511
1800
|
.split("/")
|
|
1512
1801
|
.filter(Boolean)
|
|
1513
|
-
.map((part) =>
|
|
1802
|
+
.map((part) => {
|
|
1803
|
+
// Remove braces and special chars
|
|
1804
|
+
let cleaned = part
|
|
1805
|
+
.replace(/[{}]/g, "")
|
|
1806
|
+
.replace(/[\-\.@]/g, (m) => m === "-" ? "_" : m === "." ? "__" : "_at_");
|
|
1807
|
+
if (clean) {
|
|
1808
|
+
// Remove other special chars
|
|
1809
|
+
cleaned = cleaned.replace(/[^a-zA-Z0-9_]/g, "");
|
|
1810
|
+
}
|
|
1811
|
+
// Convert to PascalCase
|
|
1812
|
+
return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
|
|
1813
|
+
})
|
|
1514
1814
|
.join("");
|
|
1515
|
-
|
|
1815
|
+
if (!cleanPath) {
|
|
1816
|
+
cleanPath = "Root";
|
|
1817
|
+
}
|
|
1818
|
+
const methodPrefix = method.toLowerCase();
|
|
1819
|
+
return `${methodPrefix}${cleanPath}`;
|
|
1516
1820
|
};
|
|
1517
1821
|
exports.default = Router;
|
|
1518
1822
|
//# sourceMappingURL=router.js.map
|