azurajs 3.0.2 → 3.0.4
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 +32 -0
- package/dist/IpResolver-BVgnGnpf.d.mts +5 -0
- package/dist/IpResolver-BVgnGnpf.d.ts +5 -0
- package/dist/SwaggerPlugin-C0UZTjaZ.d.ts +6 -0
- package/dist/SwaggerPlugin-wr9S4SRG.d.mts +6 -0
- package/dist/cookies/index.d.mts +7 -0
- package/dist/cookies/index.d.ts +7 -0
- package/dist/cookies/index.js +38 -0
- package/dist/cookies/index.js.map +1 -0
- package/dist/cookies/index.mjs +35 -0
- package/dist/cookies/index.mjs.map +1 -0
- package/dist/core/index.d.mts +18 -27
- package/dist/core/index.d.ts +18 -27
- package/dist/core/index.js +214 -14
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +214 -14
- package/dist/core/index.mjs.map +1 -1
- package/dist/cors/index.d.mts +7 -0
- package/dist/cors/index.d.ts +7 -0
- package/dist/cors/index.js +52 -0
- package/dist/cors/index.js.map +1 -0
- package/dist/cors/index.mjs +50 -0
- package/dist/cors/index.mjs.map +1 -0
- package/dist/decorators/index.d.mts +2 -0
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.js +25 -0
- package/dist/decorators/index.js.map +1 -1
- package/dist/decorators/index.mjs +24 -1
- package/dist/decorators/index.mjs.map +1 -1
- package/dist/decorators-B6l3CbxC.d.ts +13 -0
- package/dist/decorators-D5nY109r.d.mts +13 -0
- package/dist/http-error/index.d.mts +18 -0
- package/dist/http-error/index.d.ts +18 -0
- package/dist/http-error/index.js +81 -0
- package/dist/http-error/index.js.map +1 -0
- package/dist/http-error/index.mjs +79 -0
- package/dist/http-error/index.mjs.map +1 -0
- package/dist/index-j6QGMhZU.d.mts +30 -0
- package/dist/index-tpPZS_UK.d.ts +30 -0
- package/dist/index.d.mts +16 -5
- package/dist/index.d.ts +16 -5
- package/dist/index.js +1178 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1176 -15
- package/dist/index.mjs.map +1 -1
- package/dist/infra/index.d.mts +6 -0
- package/dist/infra/index.d.ts +6 -0
- package/dist/infra/index.js +162 -0
- package/dist/infra/index.js.map +1 -0
- package/dist/infra/index.mjs +159 -0
- package/dist/infra/index.mjs.map +1 -0
- package/dist/{Logger-iEQNVVSc.d.mts → logger/index.d.mts} +2 -1
- package/dist/{Logger-iEQNVVSc.d.ts → logger/index.d.ts} +2 -1
- package/dist/logger/index.js +123 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/index.mjs +120 -0
- package/dist/logger/index.mjs.map +1 -0
- package/dist/plugins/index.d.mts +8 -6
- package/dist/plugins/index.d.ts +8 -6
- package/dist/plugins/index.js +1101 -0
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/index.mjs +1101 -1
- package/dist/plugins/index.mjs.map +1 -1
- package/dist/rate-limit/index.d.mts +7 -0
- package/dist/rate-limit/index.d.ts +7 -0
- package/dist/rate-limit/index.js +81 -0
- package/dist/rate-limit/index.js.map +1 -0
- package/dist/rate-limit/index.mjs +79 -0
- package/dist/rate-limit/index.mjs.map +1 -0
- package/dist/router/index.d.mts +4 -0
- package/dist/router/index.d.ts +4 -0
- package/dist/router/index.js +218 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/index.mjs +216 -0
- package/dist/router/index.mjs.map +1 -0
- package/dist/routes.type-DZO5VBW2.d.mts +58 -0
- package/dist/routes.type-DzHNkCag.d.ts +58 -0
- package/dist/swagger/index.d.mts +30 -0
- package/dist/swagger/index.d.ts +30 -0
- package/dist/swagger/index.js +1136 -0
- package/dist/swagger/index.js.map +1 -0
- package/dist/swagger/index.mjs +1126 -0
- package/dist/swagger/index.mjs.map +1 -0
- package/dist/swagger/swagger-ui-modern.html +894 -0
- package/dist/swagger.type-Bfn5nGR8.d.mts +42 -0
- package/dist/swagger.type-CfDbFCZC.d.ts +42 -0
- package/dist/types/index.d.mts +5 -58
- package/dist/types/index.d.ts +5 -58
- package/dist/utils/index.d.mts +7 -72
- package/dist/utils/index.d.ts +7 -72
- package/dist/validators/index.d.mts +48 -0
- package/dist/validators/index.d.ts +48 -0
- package/dist/validators/index.js +144 -0
- package/dist/validators/index.js.map +1 -0
- package/dist/validators/index.mjs +141 -0
- package/dist/validators/index.mjs.map +1 -0
- package/package.json +86 -2
- package/src/cookies/index.ts +1 -0
- package/src/core/index.ts +1 -0
- package/src/core/router.ts +26 -15
- package/src/core/server.ts +64 -14
- package/src/cors/index.ts +2 -0
- package/src/decorators/index.ts +2 -0
- package/src/http-error/index.ts +1 -0
- package/src/infra/index.ts +3 -0
- package/src/logger/index.ts +1 -0
- package/src/plugins/SwaggerPlugin.ts +45 -0
- package/src/plugins/index.ts +1 -0
- package/src/rate-limit/index.ts +2 -0
- package/src/router/index.ts +1 -0
- package/src/swagger/constants.ts +8 -0
- package/src/swagger/decorators.ts +35 -0
- package/src/swagger/index.ts +10 -0
- package/src/swagger/openapi-builder.ts +199 -0
- package/src/swagger/swagger-ui-html.ts +24 -0
- package/src/swagger/swagger-ui-modern.html +894 -0
- package/src/swagger/swagger-ui-template.ts +5 -0
- package/src/types/index.ts +7 -0
- package/src/types/swagger.type.ts +36 -0
- package/src/validators/index.ts +4 -0
package/dist/index.js
CHANGED
|
@@ -187,31 +187,37 @@ var Router = class {
|
|
|
187
187
|
return path;
|
|
188
188
|
}
|
|
189
189
|
getAllRoutes() {
|
|
190
|
+
return this.getRouteDocuments().map(({ method, path }) => ({ method, path }));
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Todas as rotas com metadados (ex.: OpenAPI) associados ao handler.
|
|
194
|
+
*/
|
|
195
|
+
getRouteDocuments() {
|
|
190
196
|
const routes = [];
|
|
191
|
-
for (const [key] of this.staticRoutes) {
|
|
197
|
+
for (const [key, stored] of this.staticRoutes) {
|
|
192
198
|
const [method, path] = key.split(":", 2);
|
|
193
|
-
routes.push({ method, path });
|
|
199
|
+
routes.push({ method, path, meta: stored.meta });
|
|
194
200
|
}
|
|
195
|
-
this.
|
|
201
|
+
this.collectRoutesWithMeta(this.root, "", routes);
|
|
196
202
|
return routes;
|
|
197
203
|
}
|
|
198
|
-
|
|
199
|
-
for (const [method] of node.handlers) {
|
|
200
|
-
routes.push({ method, path: prefix || "/" });
|
|
204
|
+
collectRoutesWithMeta(node, prefix, routes) {
|
|
205
|
+
for (const [method, stored] of node.handlers) {
|
|
206
|
+
routes.push({ method, path: prefix || "/", meta: stored.meta });
|
|
201
207
|
}
|
|
202
208
|
for (const [seg, child] of node.children) {
|
|
203
|
-
this.
|
|
209
|
+
this.collectRoutesWithMeta(child, `${prefix}/${seg}`, routes);
|
|
204
210
|
}
|
|
205
211
|
if (node.paramChild) {
|
|
206
|
-
this.
|
|
212
|
+
this.collectRoutesWithMeta(
|
|
207
213
|
node.paramChild,
|
|
208
214
|
`${prefix}/:${node.paramChild.paramName}`,
|
|
209
215
|
routes
|
|
210
216
|
);
|
|
211
217
|
}
|
|
212
218
|
if (node.wildcardHandler) {
|
|
213
|
-
for (const [method] of node.wildcardHandler) {
|
|
214
|
-
routes.push({ method, path: `${prefix}
|
|
219
|
+
for (const [method, stored] of node.wildcardHandler) {
|
|
220
|
+
routes.push({ method, path: `${prefix}/*`, meta: stored.meta });
|
|
215
221
|
}
|
|
216
222
|
}
|
|
217
223
|
}
|
|
@@ -642,7 +648,174 @@ function clearCookieHeader(name, options = {}) {
|
|
|
642
648
|
});
|
|
643
649
|
}
|
|
644
650
|
|
|
651
|
+
// src/swagger/constants.ts
|
|
652
|
+
var API_TAGS_KEY = "azura:api-tags";
|
|
653
|
+
var METHOD_TAGS_KEY = "azura:swagger-method-tags";
|
|
654
|
+
var API_OPERATION_KEY = "azura:swagger-operation";
|
|
655
|
+
|
|
656
|
+
// src/swagger/openapi-builder.ts
|
|
657
|
+
function resolveInfo(options) {
|
|
658
|
+
return options.info ?? {
|
|
659
|
+
title: "AzuraJS API",
|
|
660
|
+
version: "1.0.0",
|
|
661
|
+
description: "Generated OpenAPI specification"
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function toOpenApiPath(path) {
|
|
665
|
+
if (path === "/") return "/";
|
|
666
|
+
const mapped = path.split("/").map((seg) => {
|
|
667
|
+
if (seg === "*") return "{wildcard}";
|
|
668
|
+
return seg.startsWith(":") ? `{${seg.slice(1)}}` : seg;
|
|
669
|
+
}).join("/").replace(/\/{2,}/g, "/");
|
|
670
|
+
if (mapped.endsWith("/*")) {
|
|
671
|
+
return mapped.slice(0, -2) + "/{wildcard}";
|
|
672
|
+
}
|
|
673
|
+
return mapped;
|
|
674
|
+
}
|
|
675
|
+
function pathParametersFromTemplate(path) {
|
|
676
|
+
const re = /\{([^}]+)\}/g;
|
|
677
|
+
const out = [];
|
|
678
|
+
let m;
|
|
679
|
+
const seen = /* @__PURE__ */ new Set();
|
|
680
|
+
while ((m = re.exec(path)) !== null) {
|
|
681
|
+
const name = m[1];
|
|
682
|
+
if (seen.has(name)) continue;
|
|
683
|
+
seen.add(name);
|
|
684
|
+
out.push({
|
|
685
|
+
name,
|
|
686
|
+
in: "path",
|
|
687
|
+
required: true,
|
|
688
|
+
schema: { type: "string" }
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
return out;
|
|
692
|
+
}
|
|
693
|
+
function defaultResponses(meta) {
|
|
694
|
+
if (meta?.responses && Object.keys(meta.responses).length > 0) {
|
|
695
|
+
const out = {};
|
|
696
|
+
for (const [code, r] of Object.entries(meta.responses)) {
|
|
697
|
+
out[String(code)] = {
|
|
698
|
+
description: r.description,
|
|
699
|
+
...r.schema ? { content: { "application/json": { schema: r.schema } } } : {}
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
return out;
|
|
703
|
+
}
|
|
704
|
+
return {
|
|
705
|
+
"200": {
|
|
706
|
+
description: "Successful response",
|
|
707
|
+
content: {
|
|
708
|
+
"application/json": {
|
|
709
|
+
schema: { type: "object", additionalProperties: true }
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
function mergeParameters(meta, pathParams) {
|
|
716
|
+
const fromMeta = meta?.parameters ?? [];
|
|
717
|
+
const names = new Set(pathParams.map((p) => p.name));
|
|
718
|
+
const rest = fromMeta.filter((p) => !(p.in === "path" && names.has(p.name)));
|
|
719
|
+
const mapped = fromMeta.filter((p) => p.in === "path").map((p) => ({
|
|
720
|
+
name: p.name,
|
|
721
|
+
in: "path",
|
|
722
|
+
required: p.required ?? true,
|
|
723
|
+
description: p.description,
|
|
724
|
+
schema: p.schema ?? { type: p.type ?? "string" }
|
|
725
|
+
}));
|
|
726
|
+
const pathMerged = [...pathParams];
|
|
727
|
+
for (const m of mapped) {
|
|
728
|
+
if (!pathMerged.find((x) => x.name === m.name)) pathMerged.push(m);
|
|
729
|
+
}
|
|
730
|
+
return [...pathMerged, ...rest.map((p) => mapParameter(p))];
|
|
731
|
+
}
|
|
732
|
+
function mapParameter(p) {
|
|
733
|
+
const base = {
|
|
734
|
+
name: p.name,
|
|
735
|
+
in: p.in,
|
|
736
|
+
required: p.required ?? (p.in === "path" ? true : false),
|
|
737
|
+
description: p.description
|
|
738
|
+
};
|
|
739
|
+
if (p.in === "body") {
|
|
740
|
+
return {
|
|
741
|
+
...base,
|
|
742
|
+
content: {
|
|
743
|
+
"application/json": {
|
|
744
|
+
schema: p.schema ?? { type: "object" }
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
return {
|
|
750
|
+
...base,
|
|
751
|
+
schema: p.schema ?? { type: p.type ?? "string" }
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
function operationIdFor(method, oPath) {
|
|
755
|
+
const slug = oPath.replace(/[/{}\-*]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
|
|
756
|
+
return `${method.toLowerCase()}_${slug || "root"}`;
|
|
757
|
+
}
|
|
758
|
+
function operationForMethod(method, oPath, meta) {
|
|
759
|
+
const op = {
|
|
760
|
+
operationId: operationIdFor(method, oPath),
|
|
761
|
+
summary: meta?.summary ?? `${method} ${oPath}`,
|
|
762
|
+
...meta?.description ? { description: meta.description } : {},
|
|
763
|
+
...meta?.deprecated ? { deprecated: true } : {},
|
|
764
|
+
...meta?.tags?.length ? { tags: meta.tags } : {},
|
|
765
|
+
...meta?.security?.length ? { security: meta.security } : {}
|
|
766
|
+
};
|
|
767
|
+
if (["POST", "PUT", "PATCH"].includes(method) && !meta?.parameters?.some((x) => x.in === "body")) {
|
|
768
|
+
op.requestBody = {
|
|
769
|
+
content: {
|
|
770
|
+
"application/json": {
|
|
771
|
+
schema: { type: "object", additionalProperties: true }
|
|
772
|
+
}
|
|
773
|
+
},
|
|
774
|
+
required: false
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
op.responses = defaultResponses(meta);
|
|
778
|
+
return op;
|
|
779
|
+
}
|
|
780
|
+
function buildOpenApiDocument(routes, options) {
|
|
781
|
+
const info = resolveInfo(options);
|
|
782
|
+
const prefix = options.pathPrefix ?? "";
|
|
783
|
+
const paths = {};
|
|
784
|
+
const seen = /* @__PURE__ */ new Set();
|
|
785
|
+
for (const r of routes) {
|
|
786
|
+
const full = prefix + r.path;
|
|
787
|
+
const oPath = toOpenApiPath(full);
|
|
788
|
+
const key = `${r.method}:${oPath}`;
|
|
789
|
+
if (seen.has(key)) continue;
|
|
790
|
+
seen.add(key);
|
|
791
|
+
const pathParams = pathParametersFromTemplate(oPath);
|
|
792
|
+
const op = operationForMethod(r.method, oPath, r.meta);
|
|
793
|
+
const parameters = mergeParameters(r.meta, pathParams);
|
|
794
|
+
if (parameters.length) op.parameters = parameters;
|
|
795
|
+
const methodLc = r.method.toLowerCase();
|
|
796
|
+
if (!paths[oPath]) paths[oPath] = {};
|
|
797
|
+
paths[oPath][methodLc] = op;
|
|
798
|
+
}
|
|
799
|
+
return {
|
|
800
|
+
openapi: "3.0.3",
|
|
801
|
+
info: {
|
|
802
|
+
title: info.title,
|
|
803
|
+
version: info.version,
|
|
804
|
+
...info.description ? { description: info.description } : {},
|
|
805
|
+
...info.contact ? { contact: info.contact } : {},
|
|
806
|
+
...info.license ? { license: info.license } : {}
|
|
807
|
+
},
|
|
808
|
+
...options.servers?.length ? { servers: options.servers } : {},
|
|
809
|
+
paths
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
|
|
645
813
|
// src/core/server.ts
|
|
814
|
+
function isRouteDocumentMeta(o) {
|
|
815
|
+
if (!o || typeof o !== "object" || Array.isArray(o)) return false;
|
|
816
|
+
const x = o;
|
|
817
|
+
return "summary" in x || "description" in x || "tags" in x || "deprecated" in x || "parameters" in x || "responses" in x || "security" in x || "produces" in x || "consumes" in x;
|
|
818
|
+
}
|
|
646
819
|
var CONTROLLER_META_KEY = "azura:controller";
|
|
647
820
|
var ROUTES_META_KEY = "azura:routes";
|
|
648
821
|
var PARAMS_META_KEY = "azura:params";
|
|
@@ -731,9 +904,19 @@ var AzuraServer = class {
|
|
|
731
904
|
return this;
|
|
732
905
|
}
|
|
733
906
|
route(method, path, handlers) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
907
|
+
let meta;
|
|
908
|
+
let list = handlers;
|
|
909
|
+
const last = list[list.length - 1];
|
|
910
|
+
if (list.length >= 2 && isRouteDocumentMeta(last)) {
|
|
911
|
+
meta = last;
|
|
912
|
+
list = list.slice(0, -1);
|
|
913
|
+
}
|
|
914
|
+
if (list.length === 0) {
|
|
915
|
+
throw new Error(`Route ${method} ${path} requires a handler function`);
|
|
916
|
+
}
|
|
917
|
+
const routeHandler = list[list.length - 1];
|
|
918
|
+
const middlewares = list.slice(0, -1);
|
|
919
|
+
this.router.add(method, path, routeHandler, middlewares, meta);
|
|
737
920
|
return this;
|
|
738
921
|
}
|
|
739
922
|
register(...controllers) {
|
|
@@ -746,6 +929,7 @@ var AzuraServer = class {
|
|
|
746
929
|
const instance = new Controller2();
|
|
747
930
|
const prefix = Reflect.getMetadata?.(CONTROLLER_META_KEY, Controller2) ?? "";
|
|
748
931
|
const controllerMiddlewares = Reflect.getMetadata?.("azura:middlewares", Controller2) ?? [];
|
|
932
|
+
const controllerTags = Reflect.getMetadata?.(API_TAGS_KEY, Controller2) ?? [];
|
|
749
933
|
const prototype = Controller2.prototype;
|
|
750
934
|
const propertyNames = Object.getOwnPropertyNames(prototype).filter(
|
|
751
935
|
(p) => p !== "constructor"
|
|
@@ -761,6 +945,14 @@ var AzuraServer = class {
|
|
|
761
945
|
const paramsMeta = Reflect.getMetadata?.(PARAMS_META_KEY, prototype, propertyKey) ?? [];
|
|
762
946
|
const routeMiddlewares = Reflect.getMetadata?.("azura:middlewares", prototype, propertyKey) ?? [];
|
|
763
947
|
const allMiddlewares = [...controllerMiddlewares, ...routeMiddlewares];
|
|
948
|
+
const methodTags = Reflect.getMetadata?.(METHOD_TAGS_KEY, prototype, propertyKey) ?? [];
|
|
949
|
+
const opExtra = Reflect.getMetadata?.(API_OPERATION_KEY, prototype, propertyKey) ?? {};
|
|
950
|
+
const tagList = [...controllerTags, ...methodTags, ...routeMeta.meta?.tags ?? []];
|
|
951
|
+
const mergedMeta = (() => {
|
|
952
|
+
const m = { ...routeMeta.meta, ...opExtra };
|
|
953
|
+
if (tagList.length) m.tags = tagList;
|
|
954
|
+
return Object.keys(m).length ? m : void 0;
|
|
955
|
+
})();
|
|
764
956
|
const handler = async (req, res) => {
|
|
765
957
|
const args = this.resolveParams(paramsMeta, req, res);
|
|
766
958
|
const result = await instance[propertyKey](...args);
|
|
@@ -772,7 +964,7 @@ var AzuraServer = class {
|
|
|
772
964
|
}
|
|
773
965
|
}
|
|
774
966
|
};
|
|
775
|
-
this.router.add(routeMeta.method, fullPath, handler, allMiddlewares,
|
|
967
|
+
this.router.add(routeMeta.method, fullPath, handler, allMiddlewares, mergedMeta);
|
|
776
968
|
}
|
|
777
969
|
}
|
|
778
970
|
resolveParams(paramsMeta, req, res) {
|
|
@@ -1126,6 +1318,14 @@ var AzuraServer = class {
|
|
|
1126
1318
|
getRoutes() {
|
|
1127
1319
|
return this.router.getAllRoutes();
|
|
1128
1320
|
}
|
|
1321
|
+
/** Rotas com metadados OpenAPI (para Swagger ou export manual). */
|
|
1322
|
+
getRouteDocuments() {
|
|
1323
|
+
return this.router.getRouteDocuments();
|
|
1324
|
+
}
|
|
1325
|
+
/** Gera documento OpenAPI 3.0 a partir das rotas registadas. */
|
|
1326
|
+
getOpenApiDocument(options = {}) {
|
|
1327
|
+
return buildOpenApiDocument(this.getRouteDocuments(), options);
|
|
1328
|
+
}
|
|
1129
1329
|
};
|
|
1130
1330
|
|
|
1131
1331
|
// src/decorators/Route.ts
|
|
@@ -1225,6 +1425,24 @@ function Meta(key, value) {
|
|
|
1225
1425
|
};
|
|
1226
1426
|
}
|
|
1227
1427
|
|
|
1428
|
+
// src/swagger/decorators.ts
|
|
1429
|
+
function ApiTags(...tags) {
|
|
1430
|
+
return function(target, propertyKey, _descriptor) {
|
|
1431
|
+
if (propertyKey === void 0) {
|
|
1432
|
+
Reflect.defineMetadata(API_TAGS_KEY, tags, target);
|
|
1433
|
+
} else {
|
|
1434
|
+
Reflect.defineMetadata(METHOD_TAGS_KEY, tags, target, String(propertyKey));
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
function ApiOperation(meta) {
|
|
1439
|
+
return function(target, propertyKey, _descriptor) {
|
|
1440
|
+
const key = String(propertyKey);
|
|
1441
|
+
const cur = Reflect.getMetadata?.(API_OPERATION_KEY, target, key) ?? {};
|
|
1442
|
+
Reflect.defineMetadata(API_OPERATION_KEY, { ...cur, ...meta }, target, key);
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1228
1446
|
// src/plugins/CORSPlugin.ts
|
|
1229
1447
|
var DEFAULT_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
|
|
1230
1448
|
var DEFAULT_HEADERS = ["Content-Type", "Authorization", "Accept", "X-Requested-With"];
|
|
@@ -2258,6 +2476,949 @@ function bufferIndexOf(buf, search, offset) {
|
|
|
2258
2476
|
return -1;
|
|
2259
2477
|
}
|
|
2260
2478
|
|
|
2479
|
+
// src/swagger/swagger-ui-template.ts
|
|
2480
|
+
var SWAGGER_UI_MODERN_HTML = `<!DOCTYPE html>\r
|
|
2481
|
+
<html lang="en">\r
|
|
2482
|
+
\r
|
|
2483
|
+
<head>\r
|
|
2484
|
+
<meta charset="UTF-8" />\r
|
|
2485
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0" />\r
|
|
2486
|
+
<title>__PAGE_TITLE__</title>\r
|
|
2487
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />\r
|
|
2488
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />\r
|
|
2489
|
+
<link\r
|
|
2490
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"\r
|
|
2491
|
+
rel="stylesheet" />\r
|
|
2492
|
+
<style>\r
|
|
2493
|
+
:root {\r
|
|
2494
|
+
--bg-body: #09090b;\r
|
|
2495
|
+
--bg-sidebar: #18181b;\r
|
|
2496
|
+
--bg-card: #18181b;\r
|
|
2497
|
+
--bg-card-hover: #27272a;\r
|
|
2498
|
+
--bg-input: #09090b;\r
|
|
2499
|
+
--border-color: #27272a;\r
|
|
2500
|
+
--primary-color: #6366f1;\r
|
|
2501
|
+
--primary-hover: #4f46e5;\r
|
|
2502
|
+
--text-primary: #f4f4f5;\r
|
|
2503
|
+
--text-secondary: #a1a1aa;\r
|
|
2504
|
+
--text-muted: #52525b;\r
|
|
2505
|
+
--success: #22c55e;\r
|
|
2506
|
+
--warning: #eab308;\r
|
|
2507
|
+
--danger: #ef4444;\r
|
|
2508
|
+
--info: #3b82f6;\r
|
|
2509
|
+
--font-sans: 'Inter', system-ui, -apple-system, sans-serif;\r
|
|
2510
|
+
--font-mono: 'JetBrains Mono', monospace;\r
|
|
2511
|
+
}\r
|
|
2512
|
+
\r
|
|
2513
|
+
* {\r
|
|
2514
|
+
margin: 0;\r
|
|
2515
|
+
padding: 0;\r
|
|
2516
|
+
box-sizing: border-box\r
|
|
2517
|
+
}\r
|
|
2518
|
+
\r
|
|
2519
|
+
body {\r
|
|
2520
|
+
font-family: var(--font-sans);\r
|
|
2521
|
+
background-color: var(--bg-body);\r
|
|
2522
|
+
color: var(--text-primary);\r
|
|
2523
|
+
line-height: 1.5;\r
|
|
2524
|
+
height: 100vh;\r
|
|
2525
|
+
overflow: hidden\r
|
|
2526
|
+
}\r
|
|
2527
|
+
\r
|
|
2528
|
+
::-webkit-scrollbar {\r
|
|
2529
|
+
width: 6px;\r
|
|
2530
|
+
height: 6px\r
|
|
2531
|
+
}\r
|
|
2532
|
+
\r
|
|
2533
|
+
::-webkit-scrollbar-track {\r
|
|
2534
|
+
background: transparent\r
|
|
2535
|
+
}\r
|
|
2536
|
+
\r
|
|
2537
|
+
::-webkit-scrollbar-thumb {\r
|
|
2538
|
+
background: var(--border-color);\r
|
|
2539
|
+
border-radius: 3px\r
|
|
2540
|
+
}\r
|
|
2541
|
+
\r
|
|
2542
|
+
::-webkit-scrollbar-thumb:hover {\r
|
|
2543
|
+
background: var(--text-muted)\r
|
|
2544
|
+
}\r
|
|
2545
|
+
\r
|
|
2546
|
+
.layout {\r
|
|
2547
|
+
display: flex;\r
|
|
2548
|
+
height: 100%\r
|
|
2549
|
+
}\r
|
|
2550
|
+
\r
|
|
2551
|
+
.sidebar {\r
|
|
2552
|
+
width: 300px;\r
|
|
2553
|
+
background: var(--bg-sidebar);\r
|
|
2554
|
+
border-right: 1px solid var(--border-color);\r
|
|
2555
|
+
display: flex;\r
|
|
2556
|
+
flex-direction: column;\r
|
|
2557
|
+
flex-shrink: 0\r
|
|
2558
|
+
}\r
|
|
2559
|
+
\r
|
|
2560
|
+
.sidebar-header {\r
|
|
2561
|
+
padding: 20px;\r
|
|
2562
|
+
border-bottom: 1px solid var(--border-color);\r
|
|
2563
|
+
background: var(--bg-sidebar)\r
|
|
2564
|
+
}\r
|
|
2565
|
+
\r
|
|
2566
|
+
.api-title {\r
|
|
2567
|
+
font-size: 1.25rem;\r
|
|
2568
|
+
font-weight: 700;\r
|
|
2569
|
+
color: var(--text-primary);\r
|
|
2570
|
+
margin-bottom: 4px\r
|
|
2571
|
+
}\r
|
|
2572
|
+
\r
|
|
2573
|
+
.api-version {\r
|
|
2574
|
+
font-size: 0.75rem;\r
|
|
2575
|
+
color: var(--primary-color);\r
|
|
2576
|
+
background: rgba(99, 102, 241, 0.1);\r
|
|
2577
|
+
padding: 2px 8px;\r
|
|
2578
|
+
border-radius: 12px;\r
|
|
2579
|
+
font-family: var(--font-mono)\r
|
|
2580
|
+
}\r
|
|
2581
|
+
\r
|
|
2582
|
+
.search-box {\r
|
|
2583
|
+
padding: 16px\r
|
|
2584
|
+
}\r
|
|
2585
|
+
\r
|
|
2586
|
+
.search-input {\r
|
|
2587
|
+
width: 100%;\r
|
|
2588
|
+
background: var(--bg-input);\r
|
|
2589
|
+
border: 1px solid var(--border-color);\r
|
|
2590
|
+
padding: 8px 12px;\r
|
|
2591
|
+
border-radius: 6px;\r
|
|
2592
|
+
color: var(--text-primary);\r
|
|
2593
|
+
font-size: 0.875rem;\r
|
|
2594
|
+
outline: none;\r
|
|
2595
|
+
transition: border-color 0.2s\r
|
|
2596
|
+
}\r
|
|
2597
|
+
\r
|
|
2598
|
+
.search-input:focus {\r
|
|
2599
|
+
border-color: var(--primary-color)\r
|
|
2600
|
+
}\r
|
|
2601
|
+
\r
|
|
2602
|
+
.nav-list {\r
|
|
2603
|
+
flex: 1;\r
|
|
2604
|
+
overflow-y: auto;\r
|
|
2605
|
+
padding: 0 16px 20px\r
|
|
2606
|
+
}\r
|
|
2607
|
+
\r
|
|
2608
|
+
.nav-group-title {\r
|
|
2609
|
+
font-size: 0.75rem;\r
|
|
2610
|
+
text-transform: uppercase;\r
|
|
2611
|
+
color: var(--text-muted);\r
|
|
2612
|
+
font-weight: 600;\r
|
|
2613
|
+
margin: 24px 0 8px;\r
|
|
2614
|
+
letter-spacing: 0.05em\r
|
|
2615
|
+
}\r
|
|
2616
|
+
\r
|
|
2617
|
+
.nav-item {\r
|
|
2618
|
+
display: flex;\r
|
|
2619
|
+
align-items: center;\r
|
|
2620
|
+
gap: 8px;\r
|
|
2621
|
+
padding: 8px;\r
|
|
2622
|
+
border-radius: 6px;\r
|
|
2623
|
+
cursor: pointer;\r
|
|
2624
|
+
transition: background 0.2s;\r
|
|
2625
|
+
margin-bottom: 2px\r
|
|
2626
|
+
}\r
|
|
2627
|
+
\r
|
|
2628
|
+
.nav-item:hover {\r
|
|
2629
|
+
background: var(--bg-card-hover)\r
|
|
2630
|
+
}\r
|
|
2631
|
+
\r
|
|
2632
|
+
.nav-item.active {\r
|
|
2633
|
+
background: rgba(99, 102, 241, 0.1)\r
|
|
2634
|
+
}\r
|
|
2635
|
+
\r
|
|
2636
|
+
.method-tag {\r
|
|
2637
|
+
font-family: var(--font-mono);\r
|
|
2638
|
+
font-size: 0.65rem;\r
|
|
2639
|
+
font-weight: 700;\r
|
|
2640
|
+
padding: 2px 6px;\r
|
|
2641
|
+
border-radius: 4px;\r
|
|
2642
|
+
width: 45px;\r
|
|
2643
|
+
text-align: center\r
|
|
2644
|
+
}\r
|
|
2645
|
+
\r
|
|
2646
|
+
.get {\r
|
|
2647
|
+
color: var(--success);\r
|
|
2648
|
+
background: rgba(34, 197, 94, 0.1)\r
|
|
2649
|
+
}\r
|
|
2650
|
+
\r
|
|
2651
|
+
.post {\r
|
|
2652
|
+
color: var(--primary-color);\r
|
|
2653
|
+
background: rgba(99, 102, 241, 0.1)\r
|
|
2654
|
+
}\r
|
|
2655
|
+
\r
|
|
2656
|
+
.put {\r
|
|
2657
|
+
color: var(--warning);\r
|
|
2658
|
+
background: rgba(234, 179, 8, 0.1)\r
|
|
2659
|
+
}\r
|
|
2660
|
+
\r
|
|
2661
|
+
.delete {\r
|
|
2662
|
+
color: var(--danger);\r
|
|
2663
|
+
background: rgba(239, 68, 68, 0.1)\r
|
|
2664
|
+
}\r
|
|
2665
|
+
\r
|
|
2666
|
+
.patch {\r
|
|
2667
|
+
color: var(--info);\r
|
|
2668
|
+
background: rgba(59, 130, 246, 0.1)\r
|
|
2669
|
+
}\r
|
|
2670
|
+
\r
|
|
2671
|
+
.nav-path {\r
|
|
2672
|
+
font-size: 0.8rem;\r
|
|
2673
|
+
color: var(--text-secondary);\r
|
|
2674
|
+
white-space: nowrap;\r
|
|
2675
|
+
overflow: hidden;\r
|
|
2676
|
+
text-overflow: ellipsis;\r
|
|
2677
|
+
font-family: var(--font-mono)\r
|
|
2678
|
+
}\r
|
|
2679
|
+
\r
|
|
2680
|
+
.main-content {\r
|
|
2681
|
+
flex: 1;\r
|
|
2682
|
+
overflow-y: auto;\r
|
|
2683
|
+
padding: 40px;\r
|
|
2684
|
+
scroll-behavior: smooth\r
|
|
2685
|
+
}\r
|
|
2686
|
+
\r
|
|
2687
|
+
.content-width {\r
|
|
2688
|
+
max-width: 1000px;\r
|
|
2689
|
+
margin: 0 auto\r
|
|
2690
|
+
}\r
|
|
2691
|
+
\r
|
|
2692
|
+
.info-section {\r
|
|
2693
|
+
margin-bottom: 40px\r
|
|
2694
|
+
}\r
|
|
2695
|
+
\r
|
|
2696
|
+
.info-title {\r
|
|
2697
|
+
font-size: 2.5rem;\r
|
|
2698
|
+
font-weight: 800;\r
|
|
2699
|
+
margin-bottom: 16px;\r
|
|
2700
|
+
letter-spacing: -0.02em\r
|
|
2701
|
+
}\r
|
|
2702
|
+
\r
|
|
2703
|
+
.info-desc {\r
|
|
2704
|
+
color: var(--text-secondary);\r
|
|
2705
|
+
font-size: 1.1rem;\r
|
|
2706
|
+
max-width: 700px\r
|
|
2707
|
+
}\r
|
|
2708
|
+
\r
|
|
2709
|
+
.server-section {\r
|
|
2710
|
+
background: var(--bg-card);\r
|
|
2711
|
+
border: 1px solid var(--border-color);\r
|
|
2712
|
+
padding: 20px;\r
|
|
2713
|
+
border-radius: 8px;\r
|
|
2714
|
+
margin-bottom: 40px\r
|
|
2715
|
+
}\r
|
|
2716
|
+
\r
|
|
2717
|
+
.server-label {\r
|
|
2718
|
+
display: block;\r
|
|
2719
|
+
font-size: 0.875rem;\r
|
|
2720
|
+
color: var(--text-muted);\r
|
|
2721
|
+
margin-bottom: 8px\r
|
|
2722
|
+
}\r
|
|
2723
|
+
\r
|
|
2724
|
+
.server-select {\r
|
|
2725
|
+
width: 100%;\r
|
|
2726
|
+
background: var(--bg-input);\r
|
|
2727
|
+
border: 1px solid var(--border-color);\r
|
|
2728
|
+
color: var(--text-primary);\r
|
|
2729
|
+
padding: 10px;\r
|
|
2730
|
+
border-radius: 6px;\r
|
|
2731
|
+
font-family: var(--font-mono);\r
|
|
2732
|
+
font-size: 0.9rem\r
|
|
2733
|
+
}\r
|
|
2734
|
+
\r
|
|
2735
|
+
.endpoint-card {\r
|
|
2736
|
+
background: var(--bg-card);\r
|
|
2737
|
+
border: 1px solid var(--border-color);\r
|
|
2738
|
+
border-radius: 8px;\r
|
|
2739
|
+
margin-bottom: 24px;\r
|
|
2740
|
+
overflow: hidden;\r
|
|
2741
|
+
transition: border-color 0.2s\r
|
|
2742
|
+
}\r
|
|
2743
|
+
\r
|
|
2744
|
+
.endpoint-card:hover {\r
|
|
2745
|
+
border-color: var(--text-muted)\r
|
|
2746
|
+
}\r
|
|
2747
|
+
\r
|
|
2748
|
+
.card-header {\r
|
|
2749
|
+
padding: 16px 24px;\r
|
|
2750
|
+
display: flex;\r
|
|
2751
|
+
align-items: center;\r
|
|
2752
|
+
gap: 16px;\r
|
|
2753
|
+
cursor: pointer;\r
|
|
2754
|
+
background: rgba(255, 255, 255, 0.02);\r
|
|
2755
|
+
user-select: none\r
|
|
2756
|
+
}\r
|
|
2757
|
+
\r
|
|
2758
|
+
.card-summary {\r
|
|
2759
|
+
flex: 1;\r
|
|
2760
|
+
font-weight: 500;\r
|
|
2761
|
+
font-size: 1rem\r
|
|
2762
|
+
}\r
|
|
2763
|
+
\r
|
|
2764
|
+
.card-path {\r
|
|
2765
|
+
font-family: var(--font-mono);\r
|
|
2766
|
+
font-size: 0.85rem;\r
|
|
2767
|
+
color: var(--text-muted)\r
|
|
2768
|
+
}\r
|
|
2769
|
+
\r
|
|
2770
|
+
.chevron {\r
|
|
2771
|
+
transition: transform 0.2s;\r
|
|
2772
|
+
color: var(--text-muted)\r
|
|
2773
|
+
}\r
|
|
2774
|
+
\r
|
|
2775
|
+
.card-header[aria-expanded="true"] .chevron {\r
|
|
2776
|
+
transform: rotate(180deg)\r
|
|
2777
|
+
}\r
|
|
2778
|
+
\r
|
|
2779
|
+
.card-body {\r
|
|
2780
|
+
border-top: 1px solid var(--border-color);\r
|
|
2781
|
+
padding: 24px;\r
|
|
2782
|
+
display: none\r
|
|
2783
|
+
}\r
|
|
2784
|
+
\r
|
|
2785
|
+
.card-header[aria-expanded="true"]+.card-body {\r
|
|
2786
|
+
display: block\r
|
|
2787
|
+
}\r
|
|
2788
|
+
\r
|
|
2789
|
+
.section-header {\r
|
|
2790
|
+
font-size: 0.75rem;\r
|
|
2791
|
+
text-transform: uppercase;\r
|
|
2792
|
+
color: var(--text-muted);\r
|
|
2793
|
+
font-weight: 700;\r
|
|
2794
|
+
margin: 24px 0 12px;\r
|
|
2795
|
+
letter-spacing: 0.05em\r
|
|
2796
|
+
}\r
|
|
2797
|
+
\r
|
|
2798
|
+
.section-header:first-child {\r
|
|
2799
|
+
margin-top: 0\r
|
|
2800
|
+
}\r
|
|
2801
|
+
\r
|
|
2802
|
+
.params-table {\r
|
|
2803
|
+
width: 100%;\r
|
|
2804
|
+
border-collapse: collapse;\r
|
|
2805
|
+
font-size: 0.9rem;\r
|
|
2806
|
+
margin-bottom: 20px\r
|
|
2807
|
+
}\r
|
|
2808
|
+
\r
|
|
2809
|
+
.params-table th {\r
|
|
2810
|
+
text-align: left;\r
|
|
2811
|
+
color: var(--text-muted);\r
|
|
2812
|
+
padding: 8px 12px;\r
|
|
2813
|
+
font-weight: 500;\r
|
|
2814
|
+
border-bottom: 1px solid var(--border-color);\r
|
|
2815
|
+
font-size: 0.8rem\r
|
|
2816
|
+
}\r
|
|
2817
|
+
\r
|
|
2818
|
+
.params-table td {\r
|
|
2819
|
+
padding: 12px;\r
|
|
2820
|
+
border-bottom: 1px solid var(--border-color);\r
|
|
2821
|
+
color: var(--text-secondary);\r
|
|
2822
|
+
vertical-align: top\r
|
|
2823
|
+
}\r
|
|
2824
|
+
\r
|
|
2825
|
+
.params-table tr:last-child td {\r
|
|
2826
|
+
border-bottom: none\r
|
|
2827
|
+
}\r
|
|
2828
|
+
\r
|
|
2829
|
+
.param-name {\r
|
|
2830
|
+
font-family: var(--font-mono);\r
|
|
2831
|
+
color: var(--text-primary);\r
|
|
2832
|
+
font-weight: 600\r
|
|
2833
|
+
}\r
|
|
2834
|
+
\r
|
|
2835
|
+
.param-req {\r
|
|
2836
|
+
color: var(--danger);\r
|
|
2837
|
+
font-size: 0.7rem;\r
|
|
2838
|
+
margin-left: 4px\r
|
|
2839
|
+
}\r
|
|
2840
|
+
\r
|
|
2841
|
+
.param-meta {\r
|
|
2842
|
+
font-size: 0.8rem;\r
|
|
2843
|
+
color: var(--text-muted);\r
|
|
2844
|
+
margin-top: 4px;\r
|
|
2845
|
+
font-family: var(--font-mono)\r
|
|
2846
|
+
}\r
|
|
2847
|
+
\r
|
|
2848
|
+
.code-block {\r
|
|
2849
|
+
background: #000;\r
|
|
2850
|
+
border: 1px solid var(--border-color);\r
|
|
2851
|
+
border-radius: 6px;\r
|
|
2852
|
+
padding: 16px;\r
|
|
2853
|
+
font-family: var(--font-mono);\r
|
|
2854
|
+
font-size: 0.85rem;\r
|
|
2855
|
+
overflow-x: auto;\r
|
|
2856
|
+
color: #d4d4d8\r
|
|
2857
|
+
}\r
|
|
2858
|
+
\r
|
|
2859
|
+
.try-it-box {\r
|
|
2860
|
+
background: rgba(99, 102, 241, 0.05);\r
|
|
2861
|
+
border: 1px solid rgba(99, 102, 241, 0.2);\r
|
|
2862
|
+
border-radius: 8px;\r
|
|
2863
|
+
padding: 20px;\r
|
|
2864
|
+
margin-top: 32px\r
|
|
2865
|
+
}\r
|
|
2866
|
+
\r
|
|
2867
|
+
.form-grid {\r
|
|
2868
|
+
display: grid;\r
|
|
2869
|
+
grid-template-columns: 1fr;\r
|
|
2870
|
+
gap: 16px;\r
|
|
2871
|
+
margin-bottom: 20px\r
|
|
2872
|
+
}\r
|
|
2873
|
+
\r
|
|
2874
|
+
.form-group label {\r
|
|
2875
|
+
display: block;\r
|
|
2876
|
+
font-size: 0.8rem;\r
|
|
2877
|
+
font-weight: 600;\r
|
|
2878
|
+
margin-bottom: 6px;\r
|
|
2879
|
+
color: var(--text-secondary)\r
|
|
2880
|
+
}\r
|
|
2881
|
+
\r
|
|
2882
|
+
.form-group input {\r
|
|
2883
|
+
width: 100%;\r
|
|
2884
|
+
background: var(--bg-body);\r
|
|
2885
|
+
border: 1px solid var(--border-color);\r
|
|
2886
|
+
padding: 8px 12px;\r
|
|
2887
|
+
border-radius: 4px;\r
|
|
2888
|
+
color: var(--text-primary);\r
|
|
2889
|
+
font-family: var(--font-mono);\r
|
|
2890
|
+
font-size: 0.85rem\r
|
|
2891
|
+
}\r
|
|
2892
|
+
\r
|
|
2893
|
+
.form-group input:focus {\r
|
|
2894
|
+
border-color: var(--primary-color);\r
|
|
2895
|
+
outline: none\r
|
|
2896
|
+
}\r
|
|
2897
|
+
\r
|
|
2898
|
+
.body-editor {\r
|
|
2899
|
+
width: 100%;\r
|
|
2900
|
+
height: 200px;\r
|
|
2901
|
+
background: var(--bg-body);\r
|
|
2902
|
+
border: 1px solid var(--border-color);\r
|
|
2903
|
+
border-radius: 6px;\r
|
|
2904
|
+
padding: 12px;\r
|
|
2905
|
+
color: var(--text-primary);\r
|
|
2906
|
+
font-family: var(--font-mono);\r
|
|
2907
|
+
font-size: 0.85rem;\r
|
|
2908
|
+
resize: vertical;\r
|
|
2909
|
+
margin-bottom: 16px\r
|
|
2910
|
+
}\r
|
|
2911
|
+
\r
|
|
2912
|
+
.action-btn {\r
|
|
2913
|
+
background: var(--primary-color);\r
|
|
2914
|
+
color: white;\r
|
|
2915
|
+
border: none;\r
|
|
2916
|
+
padding: 10px 20px;\r
|
|
2917
|
+
border-radius: 6px;\r
|
|
2918
|
+
font-weight: 600;\r
|
|
2919
|
+
cursor: pointer;\r
|
|
2920
|
+
font-size: 0.9rem;\r
|
|
2921
|
+
transition: background 0.2s;\r
|
|
2922
|
+
display: inline-flex;\r
|
|
2923
|
+
align-items: center;\r
|
|
2924
|
+
gap: 8px\r
|
|
2925
|
+
}\r
|
|
2926
|
+
\r
|
|
2927
|
+
.action-btn:hover {\r
|
|
2928
|
+
background: var(--primary-hover)\r
|
|
2929
|
+
}\r
|
|
2930
|
+
\r
|
|
2931
|
+
.action-btn:disabled {\r
|
|
2932
|
+
opacity: 0.6;\r
|
|
2933
|
+
cursor: not-allowed\r
|
|
2934
|
+
}\r
|
|
2935
|
+
\r
|
|
2936
|
+
.response-area {\r
|
|
2937
|
+
margin-top: 20px;\r
|
|
2938
|
+
border-top: 1px solid var(--border-color);\r
|
|
2939
|
+
padding-top: 20px\r
|
|
2940
|
+
}\r
|
|
2941
|
+
\r
|
|
2942
|
+
.status-badge {\r
|
|
2943
|
+
display: inline-block;\r
|
|
2944
|
+
padding: 4px 8px;\r
|
|
2945
|
+
border-radius: 4px;\r
|
|
2946
|
+
font-size: 0.8rem;\r
|
|
2947
|
+
font-weight: 700;\r
|
|
2948
|
+
margin-bottom: 10px;\r
|
|
2949
|
+
font-family: var(--font-mono)\r
|
|
2950
|
+
}\r
|
|
2951
|
+
\r
|
|
2952
|
+
.status-2xx {\r
|
|
2953
|
+
background: rgba(34, 197, 94, 0.2);\r
|
|
2954
|
+
color: var(--success)\r
|
|
2955
|
+
}\r
|
|
2956
|
+
\r
|
|
2957
|
+
.status-4xx,\r
|
|
2958
|
+
.status-5xx {\r
|
|
2959
|
+
background: rgba(239, 68, 68, 0.2);\r
|
|
2960
|
+
color: var(--danger)\r
|
|
2961
|
+
}\r
|
|
2962
|
+
\r
|
|
2963
|
+
.loader {\r
|
|
2964
|
+
width: 16px;\r
|
|
2965
|
+
height: 16px;\r
|
|
2966
|
+
border: 2px solid #ffffff;\r
|
|
2967
|
+
border-bottom-color: transparent;\r
|
|
2968
|
+
border-radius: 50%;\r
|
|
2969
|
+
display: inline-block;\r
|
|
2970
|
+
animation: rotation 1s linear infinite\r
|
|
2971
|
+
}\r
|
|
2972
|
+
\r
|
|
2973
|
+
@keyframes rotation {\r
|
|
2974
|
+
0% {\r
|
|
2975
|
+
transform: rotate(0deg)\r
|
|
2976
|
+
}\r
|
|
2977
|
+
\r
|
|
2978
|
+
100% {\r
|
|
2979
|
+
transform: rotate(360deg)\r
|
|
2980
|
+
}\r
|
|
2981
|
+
}\r
|
|
2982
|
+
\r
|
|
2983
|
+
@media (max-width:768px) {\r
|
|
2984
|
+
.layout {\r
|
|
2985
|
+
flex-direction: column;\r
|
|
2986
|
+
overflow: auto\r
|
|
2987
|
+
}\r
|
|
2988
|
+
\r
|
|
2989
|
+
.sidebar {\r
|
|
2990
|
+
width: 100%;\r
|
|
2991
|
+
height: auto;\r
|
|
2992
|
+
border-right: none;\r
|
|
2993
|
+
border-bottom: 1px solid var(--border-color)\r
|
|
2994
|
+
}\r
|
|
2995
|
+
\r
|
|
2996
|
+
.main-content {\r
|
|
2997
|
+
padding: 20px;\r
|
|
2998
|
+
overflow: visible\r
|
|
2999
|
+
}\r
|
|
3000
|
+
\r
|
|
3001
|
+
.nav-list {\r
|
|
3002
|
+
max-height: 300px\r
|
|
3003
|
+
}\r
|
|
3004
|
+
}\r
|
|
3005
|
+
</style>\r
|
|
3006
|
+
</head>\r
|
|
3007
|
+
\r
|
|
3008
|
+
<body>\r
|
|
3009
|
+
<div class="layout">\r
|
|
3010
|
+
<aside class="sidebar">\r
|
|
3011
|
+
<div class="sidebar-header">\r
|
|
3012
|
+
<div class="api-title" id="apiTitle">API Docs</div>\r
|
|
3013
|
+
<span class="api-version" id="apiVersion">v1.0.0</span>\r
|
|
3014
|
+
</div>\r
|
|
3015
|
+
<div class="search-box">\r
|
|
3016
|
+
<input type="text" class="search-input" id="searchNav" placeholder="Filter endpoints...">\r
|
|
3017
|
+
</div>\r
|
|
3018
|
+
<div class="nav-list" id="navList"></div>\r
|
|
3019
|
+
</aside>\r
|
|
3020
|
+
<main class="main-content">\r
|
|
3021
|
+
<div class="content-width">\r
|
|
3022
|
+
<div class="info-section">\r
|
|
3023
|
+
<h1 class="info-title" id="docTitle">API Reference</h1>\r
|
|
3024
|
+
<p class="info-desc" id="docDesc">Loading documentation...</p>\r
|
|
3025
|
+
</div>\r
|
|
3026
|
+
<div class="server-section">\r
|
|
3027
|
+
<label class="server-label">Base URL</label>\r
|
|
3028
|
+
<select class="server-select" id="serverUrl"></select>\r
|
|
3029
|
+
</div>\r
|
|
3030
|
+
<div id="endpointsContainer"></div>\r
|
|
3031
|
+
</div>\r
|
|
3032
|
+
</main>\r
|
|
3033
|
+
</div>\r
|
|
3034
|
+
\r
|
|
3035
|
+
<script>\r
|
|
3036
|
+
const state = { spec: null, server: '' };\r
|
|
3037
|
+
function sanitizeId(str) { return String(str).replace(/[^a-zA-Z0-9_-]/g, '_'); }\r
|
|
3038
|
+
async function init() {\r
|
|
3039
|
+
try {\r
|
|
3040
|
+
const res = await fetch("___AZURA_SPEC_URL_PLACEHOLDER___");\r
|
|
3041
|
+
if (!res.ok) throw new Error('Spec not found');\r
|
|
3042
|
+
state.spec = await res.json();\r
|
|
3043
|
+
renderHeader();\r
|
|
3044
|
+
renderServers();\r
|
|
3045
|
+
renderNavigation();\r
|
|
3046
|
+
renderEndpoints();\r
|
|
3047
|
+
setupSearch();\r
|
|
3048
|
+
attachGlobalListeners();\r
|
|
3049
|
+
} catch (err) {\r
|
|
3050
|
+
const descEl = document.getElementById('docDesc');\r
|
|
3051
|
+
descEl.textContent = 'Failed to load API definition. Check that the OpenAPI JSON endpoint is configured and reachable.';\r
|
|
3052
|
+
descEl.style.color = 'var(--danger)';\r
|
|
3053
|
+
}\r
|
|
3054
|
+
}\r
|
|
3055
|
+
\r
|
|
3056
|
+
function renderHeader() {\r
|
|
3057
|
+
const info = state.spec.info || {};\r
|
|
3058
|
+
document.title = \`\${info.title || 'API'} - Docs\`;\r
|
|
3059
|
+
document.getElementById('apiTitle').textContent = info.title || 'API';\r
|
|
3060
|
+
document.getElementById('apiVersion').textContent = info.version ? \`v\${info.version}\` : '';\r
|
|
3061
|
+
document.getElementById('docTitle').textContent = info.title || 'API Reference';\r
|
|
3062
|
+
document.getElementById('docDesc').textContent = info.description || 'No description provided.';\r
|
|
3063
|
+
}\r
|
|
3064
|
+
\r
|
|
3065
|
+
function renderServers() {\r
|
|
3066
|
+
const select = document.getElementById('serverUrl');\r
|
|
3067
|
+
const servers = state.spec.servers && state.spec.servers.length ? state.spec.servers : [{ url: window.location.origin, description: 'Current Origin' }];\r
|
|
3068
|
+
select.innerHTML = servers.map(s => \`<option value="\${s.url}">\${s.url}\${s.description ? \` (\${s.description})\` : ''}</option>\`).join('');\r
|
|
3069
|
+
state.server = servers[0].url;\r
|
|
3070
|
+
select.addEventListener('change', (e) => state.server = e.target.value);\r
|
|
3071
|
+
}\r
|
|
3072
|
+
\r
|
|
3073
|
+
function renderNavigation() {\r
|
|
3074
|
+
const nav = document.getElementById('navList');\r
|
|
3075
|
+
const tags = {};\r
|
|
3076
|
+
Object.entries(state.spec.paths || {}).forEach(([path, methods]) => {\r
|
|
3077
|
+
Object.entries(methods).forEach(([method, op]) => {\r
|
|
3078
|
+
const tag = (op.tags && op.tags[0]) ? op.tags[0] : 'General';\r
|
|
3079
|
+
if (!tags[tag]) tags[tag] = [];\r
|
|
3080
|
+
tags[tag].push({ path, method, op });\r
|
|
3081
|
+
});\r
|
|
3082
|
+
});\r
|
|
3083
|
+
\r
|
|
3084
|
+
let html = '';\r
|
|
3085
|
+
Object.entries(tags).forEach(([tag, items]) => {\r
|
|
3086
|
+
html += \`<div class="nav-group-title">\${tag}</div>\`;\r
|
|
3087
|
+
items.forEach(item => {\r
|
|
3088
|
+
const label = item.op.summary || item.path;\r
|
|
3089
|
+
const filter = \`\${item.method} \${item.path} \${label}\`.toLowerCase();\r
|
|
3090
|
+
const targetId = sanitizeId(\`\${item.method}-\${item.path}\`);\r
|
|
3091
|
+
html += \`<div class="nav-item" data-filter="\${escapeHtmlAttr(filter)}" data-target-id="\${escapeHtmlAttr(targetId)}">\r
|
|
3092
|
+
<span class="method-tag \${item.method.toLowerCase()}">\${item.method.toUpperCase()}</span>\r
|
|
3093
|
+
<span class="nav-path">\${escapeHtmlAttr(label)}</span>\r
|
|
3094
|
+
</div>\`;\r
|
|
3095
|
+
});\r
|
|
3096
|
+
});\r
|
|
3097
|
+
\r
|
|
3098
|
+
nav.innerHTML = html;\r
|
|
3099
|
+
nav.querySelectorAll('.nav-item').forEach(el => {\r
|
|
3100
|
+
el.addEventListener('click', () => {\r
|
|
3101
|
+
const tid = el.dataset.targetId;\r
|
|
3102
|
+
scrollEndpoint(tid);\r
|
|
3103
|
+
document.querySelectorAll('.nav-item').forEach(i => i.classList.toggle('active', i === el));\r
|
|
3104
|
+
});\r
|
|
3105
|
+
});\r
|
|
3106
|
+
}\r
|
|
3107
|
+
\r
|
|
3108
|
+
function renderEndpoints() {\r
|
|
3109
|
+
const container = document.getElementById('endpointsContainer');\r
|
|
3110
|
+
container.innerHTML = '';\r
|
|
3111
|
+
Object.entries(state.spec.paths || {}).forEach(([path, methods]) => {\r
|
|
3112
|
+
Object.entries(methods).forEach(([method, op]) => {\r
|
|
3113
|
+
container.appendChild(createEndpointCard(path, method, op));\r
|
|
3114
|
+
});\r
|
|
3115
|
+
});\r
|
|
3116
|
+
}\r
|
|
3117
|
+
\r
|
|
3118
|
+
function createEndpointCard(path, method, op) {\r
|
|
3119
|
+
const rawId = \`\${method}-\${path}\`;\r
|
|
3120
|
+
const id = sanitizeId(rawId);\r
|
|
3121
|
+
const card = document.createElement('div');\r
|
|
3122
|
+
card.className = 'endpoint-card';\r
|
|
3123
|
+
card.id = id;\r
|
|
3124
|
+
\r
|
|
3125
|
+
const descriptionHtml = op.description ? \`<p style="margin-bottom:20px;color:var(--text-secondary)">\${escapeHtml(op.description)}</p>\` : '';\r
|
|
3126
|
+
const paramsHtml = renderParameters(op.parameters);\r
|
|
3127
|
+
const requestBodyHtml = renderRequestBody(op.requestBody);\r
|
|
3128
|
+
const responsesHtml = renderResponses(op.responses);\r
|
|
3129
|
+
\r
|
|
3130
|
+
const inputsId = \`inputs_\${id}\`;\r
|
|
3131
|
+
const bodyId = \`body_\${id}\`;\r
|
|
3132
|
+
const tryButtonId = \`btn_\${id}\`;\r
|
|
3133
|
+
const inputsHtmlForTry = (op.parameters || []).map(p => {\r
|
|
3134
|
+
const placeholder = p.schema?.type || '';\r
|
|
3135
|
+
const required = p.required ? ' <span style="color:var(--danger)">*</span>' : '';\r
|
|
3136
|
+
return \`<div class="form-group">\r
|
|
3137
|
+
<label>\${escapeHtml(p.name)} <span style="font-weight:400;color:var(--text-muted)">(\${escapeHtml(p.in)})</span>\${required}</label>\r
|
|
3138
|
+
<input type="text" data-name="\${escapeHtmlAttr(p.name)}" data-in="\${escapeHtmlAttr(p.in)}" placeholder="\${escapeHtmlAttr(placeholder)}">\r
|
|
3139
|
+
</div>\`;\r
|
|
3140
|
+
}).join('');\r
|
|
3141
|
+
\r
|
|
3142
|
+
const bodyEditorHtml = op.requestBody ? \`<div class="form-group">\r
|
|
3143
|
+
<label>Request Body (JSON)</label>\r
|
|
3144
|
+
<textarea class="body-editor" id="\${bodyId}">\${getExampleBody(op.requestBody)}</textarea>\r
|
|
3145
|
+
</div>\` : '';\r
|
|
3146
|
+
\r
|
|
3147
|
+
card.innerHTML = \`\r
|
|
3148
|
+
<div class="card-header" aria-expanded="false">\r
|
|
3149
|
+
<span class="method-tag \${method.toLowerCase()}">\${method.toUpperCase()}</span>\r
|
|
3150
|
+
<span class="card-summary">\${escapeHtml(op.summary || path)}</span>\r
|
|
3151
|
+
<span class="card-path">\${escapeHtml(path)}</span>\r
|
|
3152
|
+
<svg class="chevron" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>\r
|
|
3153
|
+
</div>\r
|
|
3154
|
+
<div class="card-body">\r
|
|
3155
|
+
\${descriptionHtml}\r
|
|
3156
|
+
\${paramsHtml}\r
|
|
3157
|
+
\${requestBodyHtml}\r
|
|
3158
|
+
<div class="try-it-box">\r
|
|
3159
|
+
<div class="section-header">Try It Out</div>\r
|
|
3160
|
+
<div class="form-grid" id="\${inputsId}">\r
|
|
3161
|
+
\${inputsHtmlForTry}\r
|
|
3162
|
+
</div>\r
|
|
3163
|
+
\${bodyEditorHtml}\r
|
|
3164
|
+
<button class="action-btn" id="\${tryButtonId}" data-method="\${escapeHtmlAttr(method)}" data-path="\${escapeHtmlAttr(path)}" data-card-id="\${escapeHtmlAttr(id)}">\r
|
|
3165
|
+
<span>Execute Request</span>\r
|
|
3166
|
+
</button>\r
|
|
3167
|
+
<div class="response-area" style="display:none"></div>\r
|
|
3168
|
+
</div>\r
|
|
3169
|
+
\${responsesHtml}\r
|
|
3170
|
+
</div>\r
|
|
3171
|
+
\`;\r
|
|
3172
|
+
\r
|
|
3173
|
+
return card;\r
|
|
3174
|
+
}\r
|
|
3175
|
+
\r
|
|
3176
|
+
function renderParameters(params) {\r
|
|
3177
|
+
if (!params || params.length === 0) return '';\r
|
|
3178
|
+
return \`\r
|
|
3179
|
+
<div class="section-header">Parameters</div>\r
|
|
3180
|
+
<table class="params-table">\r
|
|
3181
|
+
<thead><tr><th>Name</th><th>Description</th></tr></thead>\r
|
|
3182
|
+
<tbody>\r
|
|
3183
|
+
\${params.map(p => \`\r
|
|
3184
|
+
<tr>\r
|
|
3185
|
+
<td>\r
|
|
3186
|
+
<div class="param-name">\${escapeHtml(p.name)}\${p.required ? '<span class="param-req">*</span>' : ''}</div>\r
|
|
3187
|
+
<div class="param-meta">\${escapeHtml(p.in)} • \${escapeHtml(p.schema?.type || 'string')}</div>\r
|
|
3188
|
+
</td>\r
|
|
3189
|
+
<td>\${escapeHtml(p.description || '-')}</td>\r
|
|
3190
|
+
</tr>\r
|
|
3191
|
+
\`).join('')}\r
|
|
3192
|
+
</tbody>\r
|
|
3193
|
+
</table>\r
|
|
3194
|
+
\`;\r
|
|
3195
|
+
}\r
|
|
3196
|
+
\r
|
|
3197
|
+
function renderRequestBody(body) {\r
|
|
3198
|
+
if (!body) return '';\r
|
|
3199
|
+
const contentType = Object.keys(body.content || {})[0] || 'application/json';\r
|
|
3200
|
+
const schema = body.content && body.content[contentType] ? body.content[contentType].schema : null;\r
|
|
3201
|
+
return \`\r
|
|
3202
|
+
<div class="section-header">Request Body <span style="font-weight:400;text-transform:none;color:var(--text-secondary)">(\${escapeHtml(contentType)})</span></div>\r
|
|
3203
|
+
<div class="code-block">\${escapeHtml(JSON.stringify(generateExample(schema), null, 2))}</div>\r
|
|
3204
|
+
\`;\r
|
|
3205
|
+
}\r
|
|
3206
|
+
\r
|
|
3207
|
+
function renderResponses(responses) {\r
|
|
3208
|
+
if (!responses) return '';\r
|
|
3209
|
+
return \`\r
|
|
3210
|
+
<div class="section-header">Responses</div>\r
|
|
3211
|
+
\${Object.entries(responses).map(([status, res]) => \`\r
|
|
3212
|
+
<div style="margin-bottom:16px">\r
|
|
3213
|
+
<div style="font-weight:600;font-family:var(--font-mono);margin-bottom:4px">\r
|
|
3214
|
+
<span style="color:\${String(status).startsWith('2') ? 'var(--success)' : 'var(--danger)'}">\${escapeHtml(status)}</span>\r
|
|
3215
|
+
<span style="color:var(--text-secondary)">\${escapeHtml(res.description || '')}</span>\r
|
|
3216
|
+
</div>\r
|
|
3217
|
+
</div>\r
|
|
3218
|
+
\`).join('')}\r
|
|
3219
|
+
\`;\r
|
|
3220
|
+
}\r
|
|
3221
|
+
\r
|
|
3222
|
+
function getExampleBody(body) {\r
|
|
3223
|
+
if (!body || !body.content) return '{}';\r
|
|
3224
|
+
const contentType = Object.keys(body.content)[0];\r
|
|
3225
|
+
const schema = body.content[contentType].schema;\r
|
|
3226
|
+
try { return JSON.stringify(generateExample(schema), null, 2); } catch { return '{}'; }\r
|
|
3227
|
+
}\r
|
|
3228
|
+
\r
|
|
3229
|
+
function generateExample(schema) {\r
|
|
3230
|
+
if (!schema) return {};\r
|
|
3231
|
+
if (schema.example !== undefined) return schema.example;\r
|
|
3232
|
+
if (schema.$ref) {\r
|
|
3233
|
+
const ref = String(schema.$ref).replace(/^#\\/components\\/schemas\\//, '');\r
|
|
3234
|
+
const comp = state.spec.components && state.spec.components.schemas && state.spec.components.schemas[ref];\r
|
|
3235
|
+
if (comp) return generateExample(comp);\r
|
|
3236
|
+
return {};\r
|
|
3237
|
+
}\r
|
|
3238
|
+
if (schema.type === 'object' && schema.properties) {\r
|
|
3239
|
+
const obj = {};\r
|
|
3240
|
+
for (const [k, prop] of Object.entries(schema.properties)) {\r
|
|
3241
|
+
obj[k] = generateExample(prop);\r
|
|
3242
|
+
}\r
|
|
3243
|
+
return obj;\r
|
|
3244
|
+
}\r
|
|
3245
|
+
if (schema.type === 'array') return [generateExample(schema.items)];\r
|
|
3246
|
+
if (schema.type === 'string') return 'string';\r
|
|
3247
|
+
if (schema.type === 'integer' || schema.type === 'number') return 0;\r
|
|
3248
|
+
if (schema.type === 'boolean') return true;\r
|
|
3249
|
+
return null;\r
|
|
3250
|
+
}\r
|
|
3251
|
+
\r
|
|
3252
|
+
async function executeRequest(method, path, btn) {\r
|
|
3253
|
+
const cardId = btn.dataset.cardId;\r
|
|
3254
|
+
const card = document.getElementById(cardId);\r
|
|
3255
|
+
const inputs = card.querySelectorAll('input[data-name]');\r
|
|
3256
|
+
const bodyEditor = card.querySelector('.body-editor');\r
|
|
3257
|
+
const responseArea = card.querySelector('.response-area');\r
|
|
3258
|
+
const originalHtml = btn.innerHTML;\r
|
|
3259
|
+
btn.disabled = true;\r
|
|
3260
|
+
btn.innerHTML = '<span class="loader"></span> Executing...';\r
|
|
3261
|
+
responseArea.style.display = 'none';\r
|
|
3262
|
+
try {\r
|
|
3263
|
+
let url = (state.server || '') + path;\r
|
|
3264
|
+
const headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' };\r
|
|
3265
|
+
const queryParams = new URLSearchParams();\r
|
|
3266
|
+
inputs.forEach(input => {\r
|
|
3267
|
+
const value = input.value.trim();\r
|
|
3268
|
+
if (!value) return;\r
|
|
3269
|
+
const type = input.dataset.in;\r
|
|
3270
|
+
const name = input.dataset.name;\r
|
|
3271
|
+
if (type === 'path') {\r
|
|
3272
|
+
url = url.replace(\`{\${name}}\`, encodeURIComponent(value));\r
|
|
3273
|
+
} else if (type === 'query') {\r
|
|
3274
|
+
queryParams.append(name, value);\r
|
|
3275
|
+
} else if (type === 'header') {\r
|
|
3276
|
+
headers[name] = value;\r
|
|
3277
|
+
} else if (type === 'cookie') {\r
|
|
3278
|
+
// cookies handled as header for demo purposes\r
|
|
3279
|
+
headers['Cookie'] = (headers['Cookie'] ? headers['Cookie'] + '; ' : '') + \`\${name}=\${value}\`;\r
|
|
3280
|
+
}\r
|
|
3281
|
+
});\r
|
|
3282
|
+
const qs = queryParams.toString();\r
|
|
3283
|
+
if (qs) url += \`?\${qs}\`;\r
|
|
3284
|
+
const options = { method: method.toUpperCase(), headers };\r
|
|
3285
|
+
if (bodyEditor && ['POST', 'PUT', 'PATCH'].includes(method.toUpperCase())) {\r
|
|
3286
|
+
try {\r
|
|
3287
|
+
const parsed = JSON.parse(bodyEditor.value);\r
|
|
3288
|
+
options.body = JSON.stringify(parsed);\r
|
|
3289
|
+
} catch (e) {\r
|
|
3290
|
+
alert('Invalid JSON in Request Body');\r
|
|
3291
|
+
throw new Error('Invalid JSON');\r
|
|
3292
|
+
}\r
|
|
3293
|
+
}\r
|
|
3294
|
+
const start = performance.now();\r
|
|
3295
|
+
const res = await fetch(url, options);\r
|
|
3296
|
+
const duration = Math.round(performance.now() - start);\r
|
|
3297
|
+
let data;\r
|
|
3298
|
+
const ct = res.headers.get('content-type') || '';\r
|
|
3299
|
+
if (ct.includes('application/json')) data = await res.json(); else data = await res.text();\r
|
|
3300
|
+
const statusClass = res.ok ? 'status-2xx' : (String(res.status).startsWith('5') ? 'status-5xx' : 'status-4xx');\r
|
|
3301
|
+
responseArea.style.display = 'block';\r
|
|
3302
|
+
responseArea.innerHTML = \`\r
|
|
3303
|
+
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">\r
|
|
3304
|
+
<span class="status-badge \${statusClass}">\${res.status} \${escapeHtml(res.statusText)}</span>\r
|
|
3305
|
+
<span style="font-family:var(--font-mono);font-size:0.8rem;color:var(--text-muted)">\${duration}ms</span>\r
|
|
3306
|
+
</div>\r
|
|
3307
|
+
<div class="section-header">Response Body</div>\r
|
|
3308
|
+
<div class="code-block">\${escapeHtml(typeof data === 'object' ? JSON.stringify(data, null, 2) : String(data))}</div>\r
|
|
3309
|
+
<div class="section-header">Response Headers</div>\r
|
|
3310
|
+
<div class="code-block">\${escapeHtml(Array.from(res.headers.entries()).map(([k, v]) => \`\${k}: \${v}\`).join('\\\\n'))}</div>\r
|
|
3311
|
+
\`;\r
|
|
3312
|
+
} catch (err) {\r
|
|
3313
|
+
responseArea.style.display = 'block';\r
|
|
3314
|
+
responseArea.innerHTML = \`<div style="color:var(--danger);font-weight:600">Error: \${escapeHtml(err.message || String(err))}</div>\`;\r
|
|
3315
|
+
} finally {\r
|
|
3316
|
+
btn.disabled = false;\r
|
|
3317
|
+
btn.innerHTML = originalHtml;\r
|
|
3318
|
+
}\r
|
|
3319
|
+
}\r
|
|
3320
|
+
\r
|
|
3321
|
+
function toggleCard(header) {\r
|
|
3322
|
+
const isExpanded = header.getAttribute('aria-expanded') === 'true';\r
|
|
3323
|
+
header.setAttribute('aria-expanded', String(!isExpanded));\r
|
|
3324
|
+
}\r
|
|
3325
|
+
\r
|
|
3326
|
+
function scrollEndpoint(sanitizedId) {\r
|
|
3327
|
+
const el = document.getElementById(sanitizedId);\r
|
|
3328
|
+
if (!el) return;\r
|
|
3329
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'start' });\r
|
|
3330
|
+
const header = el.querySelector('.card-header');\r
|
|
3331
|
+
if (header && header.getAttribute('aria-expanded') !== 'true') {\r
|
|
3332
|
+
header.setAttribute('aria-expanded', 'true');\r
|
|
3333
|
+
}\r
|
|
3334
|
+
}\r
|
|
3335
|
+
\r
|
|
3336
|
+
function setupSearch() {\r
|
|
3337
|
+
const input = document.getElementById('searchNav');\r
|
|
3338
|
+
input.addEventListener('input', (e) => {\r
|
|
3339
|
+
const val = e.target.value.toLowerCase();\r
|
|
3340
|
+
document.querySelectorAll('.nav-item').forEach(el => {\r
|
|
3341
|
+
const filter = el.dataset.filter || '';\r
|
|
3342
|
+
el.style.display = filter.includes(val) ? 'flex' : 'none';\r
|
|
3343
|
+
});\r
|
|
3344
|
+
});\r
|
|
3345
|
+
}\r
|
|
3346
|
+
\r
|
|
3347
|
+
function attachGlobalListeners() {\r
|
|
3348
|
+
document.querySelectorAll('.endpoint-card .card-header').forEach(header => {\r
|
|
3349
|
+
header.addEventListener('click', () => toggleCard(header));\r
|
|
3350
|
+
});\r
|
|
3351
|
+
document.querySelectorAll('.action-btn').forEach(btn => {\r
|
|
3352
|
+
btn.addEventListener('click', () => executeRequest(btn.dataset.method, btn.dataset.path, btn));\r
|
|
3353
|
+
});\r
|
|
3354
|
+
}\r
|
|
3355
|
+
\r
|
|
3356
|
+
function escapeHtml(str) {\r
|
|
3357
|
+
if (str === undefined || str === null) return '';\r
|
|
3358
|
+
return String(str)\r
|
|
3359
|
+
.replace(/&/g, '&')\r
|
|
3360
|
+
.replace(/</g, '<')\r
|
|
3361
|
+
.replace(/>/g, '>')\r
|
|
3362
|
+
.replace(/"/g, '"')\r
|
|
3363
|
+
.replace(/'/g, ''');\r
|
|
3364
|
+
}\r
|
|
3365
|
+
function escapeHtmlAttr(str) {\r
|
|
3366
|
+
return escapeHtml(str).replace(/"/g, '"').replace(/'/g, ''');\r
|
|
3367
|
+
}\r
|
|
3368
|
+
\r
|
|
3369
|
+
init();\r
|
|
3370
|
+
</script>\r
|
|
3371
|
+
</body>\r
|
|
3372
|
+
\r
|
|
3373
|
+
</html>`;
|
|
3374
|
+
|
|
3375
|
+
// src/swagger/swagger-ui-html.ts
|
|
3376
|
+
function renderSwaggerUiPage(options) {
|
|
3377
|
+
const { title, specUrl } = options;
|
|
3378
|
+
return SWAGGER_UI_MODERN_HTML.replaceAll("__PAGE_TITLE__", escapeHtml(title)).replaceAll(
|
|
3379
|
+
'"___AZURA_SPEC_URL_PLACEHOLDER___"',
|
|
3380
|
+
JSON.stringify(specUrl)
|
|
3381
|
+
);
|
|
3382
|
+
}
|
|
3383
|
+
function escapeHtml(s) {
|
|
3384
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
// src/plugins/SwaggerPlugin.ts
|
|
3388
|
+
function SwaggerPlugin(options) {
|
|
3389
|
+
const {
|
|
3390
|
+
getDocuments,
|
|
3391
|
+
specPath = "/openapi.json",
|
|
3392
|
+
docsPath = "/docs",
|
|
3393
|
+
deepLinking = true,
|
|
3394
|
+
servers,
|
|
3395
|
+
pathPrefix,
|
|
3396
|
+
info
|
|
3397
|
+
} = options;
|
|
3398
|
+
const title = info?.title ?? "API";
|
|
3399
|
+
return async (ctx, next) => {
|
|
3400
|
+
const { req, res } = ctx;
|
|
3401
|
+
const path = req.pathname ?? "";
|
|
3402
|
+
if (path === specPath || path === `${specPath}/`) {
|
|
3403
|
+
const doc = buildOpenApiDocument(getDocuments(), {
|
|
3404
|
+
info: info ?? { title, version: "1.0.0" },
|
|
3405
|
+
servers,
|
|
3406
|
+
pathPrefix
|
|
3407
|
+
});
|
|
3408
|
+
res.json(doc);
|
|
3409
|
+
return;
|
|
3410
|
+
}
|
|
3411
|
+
if (path === docsPath || path === `${docsPath}/`) {
|
|
3412
|
+
const html = renderSwaggerUiPage({
|
|
3413
|
+
title,
|
|
3414
|
+
specUrl: specPath});
|
|
3415
|
+
res.html(html);
|
|
3416
|
+
return;
|
|
3417
|
+
}
|
|
3418
|
+
next();
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3421
|
+
|
|
2261
3422
|
// src/utils/validators/DTOValidator.ts
|
|
2262
3423
|
var DTOValidator = class {
|
|
2263
3424
|
static validate(data, schema) {
|
|
@@ -2536,6 +3697,8 @@ function LoggingMiddleware() {
|
|
|
2536
3697
|
};
|
|
2537
3698
|
}
|
|
2538
3699
|
|
|
3700
|
+
exports.ApiOperation = ApiOperation;
|
|
3701
|
+
exports.ApiTags = ApiTags;
|
|
2539
3702
|
exports.AzuraServer = AzuraServer;
|
|
2540
3703
|
exports.Body = Body;
|
|
2541
3704
|
exports.CORSPlugin = CORSPlugin;
|
|
@@ -2580,6 +3743,7 @@ exports.SchemaValidator = SchemaValidator;
|
|
|
2580
3743
|
exports.Session = Session;
|
|
2581
3744
|
exports.SessionPlugin = SessionPlugin;
|
|
2582
3745
|
exports.StaticPlugin = StaticPlugin;
|
|
3746
|
+
exports.SwaggerPlugin = SwaggerPlugin;
|
|
2583
3747
|
exports.TimeoutPlugin = TimeoutPlugin;
|
|
2584
3748
|
exports.UseMiddleware = UseMiddleware;
|
|
2585
3749
|
exports.applyDecorators = applyDecorators;
|