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