specra 0.1.13 → 0.2.0
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/LICENSE.MD +25 -4
- package/README.md +67 -58
- package/config/specra.config.schema.json +16 -0
- package/config/svelte-config.js +63 -0
- package/dist/api-parser.types.d.ts +59 -0
- package/dist/api-parser.types.js +5 -0
- package/dist/api.types.d.ts +137 -0
- package/dist/api.types.js +5 -0
- package/dist/category.d.ts +21 -0
- package/dist/category.js +48 -0
- package/dist/components/ConfigProvider.svelte +13 -0
- package/dist/components/ConfigProvider.svelte.d.ts +31 -0
- package/dist/components/docs/Accordion.svelte +18 -0
- package/dist/components/docs/Accordion.svelte.d.ts +10 -0
- package/dist/components/docs/AccordionItem.svelte +41 -0
- package/dist/components/docs/AccordionItem.svelte.d.ts +10 -0
- package/dist/components/docs/Badge.svelte +28 -0
- package/dist/components/docs/Badge.svelte.d.ts +9 -0
- package/dist/components/docs/Breadcrumb.svelte +80 -0
- package/dist/components/docs/Breadcrumb.svelte.d.ts +8 -0
- package/dist/components/docs/Callout.svelte +96 -0
- package/dist/components/docs/Callout.svelte.d.ts +10 -0
- package/dist/components/docs/Card.svelte +63 -0
- package/dist/components/docs/Card.svelte.d.ts +12 -0
- package/dist/components/docs/CardGrid.svelte +24 -0
- package/dist/components/docs/CardGrid.svelte.d.ts +8 -0
- package/dist/components/docs/CategoryIndex.svelte +110 -0
- package/dist/components/docs/CategoryIndex.svelte.d.ts +29 -0
- package/dist/components/docs/CodeBlock.svelte +172 -0
- package/dist/components/docs/CodeBlock.svelte.d.ts +8 -0
- package/dist/components/docs/Column.svelte +25 -0
- package/dist/components/docs/Column.svelte.d.ts +8 -0
- package/dist/components/docs/Columns.svelte +38 -0
- package/dist/components/docs/Columns.svelte.d.ts +13 -0
- package/dist/components/docs/DevModeBadge.svelte +15 -0
- package/dist/components/docs/DevModeBadge.svelte.d.ts +18 -0
- package/dist/components/docs/DocBadge.svelte +28 -0
- package/dist/components/docs/DocBadge.svelte.d.ts +9 -0
- package/dist/components/docs/DocLayout.svelte +107 -0
- package/dist/components/docs/DocLayout.svelte.d.ts +32 -0
- package/dist/components/docs/DocLoading.svelte +53 -0
- package/dist/components/docs/DocLoading.svelte.d.ts +18 -0
- package/dist/components/docs/DocMetadata.svelte +106 -0
- package/dist/components/docs/DocMetadata.svelte.d.ts +18 -0
- package/dist/components/docs/DocNavigation.svelte +56 -0
- package/dist/components/docs/DocNavigation.svelte.d.ts +12 -0
- package/dist/components/docs/DocTags.svelte +22 -0
- package/dist/components/docs/DocTags.svelte.d.ts +6 -0
- package/dist/components/docs/DraftBadge.svelte +10 -0
- package/dist/components/docs/DraftBadge.svelte.d.ts +18 -0
- package/dist/components/docs/Footer.svelte +72 -0
- package/dist/components/docs/Footer.svelte.d.ts +7 -0
- package/dist/components/docs/Frame.svelte +27 -0
- package/dist/components/docs/Frame.svelte.d.ts +9 -0
- package/dist/components/docs/Header.svelte +123 -0
- package/dist/components/docs/Header.svelte.d.ts +9 -0
- package/dist/components/docs/HeaderWithMenu.svelte +34 -0
- package/dist/components/docs/HeaderWithMenu.svelte.d.ts +17 -0
- package/dist/components/docs/HotReloadIndicator.svelte +44 -0
- package/dist/components/docs/HotReloadIndicator.svelte.d.ts +3 -0
- package/dist/components/docs/Icon.svelte +103 -0
- package/dist/components/docs/Icon.svelte.d.ts +11 -0
- package/dist/components/docs/Image.svelte +88 -0
- package/dist/components/docs/Image.svelte.d.ts +11 -0
- package/dist/components/docs/ImageCard.svelte +91 -0
- package/dist/components/docs/ImageCard.svelte.d.ts +12 -0
- package/dist/components/docs/ImageCardGrid.svelte +25 -0
- package/dist/components/docs/ImageCardGrid.svelte.d.ts +8 -0
- package/dist/components/docs/LayoutProviders.svelte +57 -0
- package/dist/components/docs/LayoutProviders.svelte.d.ts +9 -0
- package/dist/components/docs/Logo.svelte +25 -0
- package/dist/components/docs/Logo.svelte.d.ts +11 -0
- package/dist/components/docs/Math.svelte +54 -0
- package/dist/components/docs/Math.svelte.d.ts +7 -0
- package/dist/components/docs/MdxContent.svelte +41 -0
- package/dist/components/docs/MdxHotReload.svelte +78 -0
- package/dist/components/docs/MdxHotReload.svelte.d.ts +9 -0
- package/dist/components/docs/MdxLayout.svelte +16 -0
- package/dist/components/docs/MdxLayout.svelte.d.ts +6 -0
- package/dist/components/docs/Mermaid.svelte +88 -0
- package/dist/components/docs/Mermaid.svelte.d.ts +7 -0
- package/dist/components/docs/MobileDocLayout.svelte +211 -0
- package/dist/components/docs/MobileDocLayout.svelte.d.ts +35 -0
- package/dist/components/docs/MobileSidebar.svelte +122 -0
- package/dist/components/docs/MobileSidebar.svelte.d.ts +31 -0
- package/dist/components/docs/MobileSidebarWrapper.svelte +122 -0
- package/dist/components/docs/MobileSidebarWrapper.svelte.d.ts +32 -0
- package/dist/components/docs/NotFoundContent.svelte +40 -0
- package/dist/components/docs/NotFoundContent.svelte.d.ts +6 -0
- package/dist/components/docs/SearchHighlight.svelte +116 -0
- package/dist/components/docs/SearchHighlight.svelte.d.ts +3 -0
- package/dist/components/docs/SearchModal.svelte +239 -0
- package/dist/components/docs/SearchModal.svelte.d.ts +9 -0
- package/dist/components/docs/Sidebar.svelte +69 -0
- package/dist/components/docs/Sidebar.svelte.d.ts +31 -0
- package/dist/components/docs/SidebarMenuItems.svelte +344 -0
- package/dist/components/docs/SidebarMenuItems.svelte.d.ts +33 -0
- package/dist/components/docs/SidebarSkeleton.svelte +50 -0
- package/dist/components/docs/SidebarSkeleton.svelte.d.ts +18 -0
- package/dist/components/docs/SiteBanner.svelte +92 -0
- package/dist/components/docs/SiteBanner.svelte.d.ts +7 -0
- package/dist/components/docs/Step.svelte +44 -0
- package/dist/components/docs/Step.svelte.d.ts +8 -0
- package/dist/components/docs/Steps.svelte +15 -0
- package/dist/components/docs/Steps.svelte.d.ts +7 -0
- package/dist/components/docs/Tab.svelte +40 -0
- package/dist/components/docs/Tab.svelte.d.ts +8 -0
- package/dist/components/docs/TabGroups.svelte +183 -0
- package/dist/components/docs/TabGroups.svelte.d.ts +25 -0
- package/dist/components/docs/TableOfContents.svelte +100 -0
- package/dist/components/docs/TableOfContents.svelte.d.ts +9 -0
- package/dist/components/docs/Tabs.svelte +69 -0
- package/dist/components/docs/Tabs.svelte.d.ts +8 -0
- package/dist/components/docs/ThemeToggle.svelte +16 -0
- package/dist/components/docs/ThemeToggle.svelte.d.ts +18 -0
- package/dist/components/docs/Tooltip.svelte +44 -0
- package/dist/components/docs/Tooltip.svelte.d.ts +10 -0
- package/dist/components/docs/VersionSwitcher.svelte +95 -0
- package/dist/components/docs/VersionSwitcher.svelte.d.ts +7 -0
- package/dist/components/docs/Video.svelte +84 -0
- package/dist/components/docs/Video.svelte.d.ts +12 -0
- package/dist/components/docs/api/ApiEndpoint.svelte +61 -0
- package/dist/components/docs/api/ApiEndpoint.svelte.d.ts +11 -0
- package/dist/components/docs/api/ApiParams.svelte +80 -0
- package/dist/components/docs/api/ApiParams.svelte.d.ts +14 -0
- package/dist/components/docs/api/ApiPlayground.svelte +259 -0
- package/dist/components/docs/api/ApiPlayground.svelte.d.ts +16 -0
- package/dist/components/docs/api/ApiReference.svelte +278 -0
- package/dist/components/docs/api/ApiReference.svelte.d.ts +23 -0
- package/dist/components/docs/api/ApiResponse.svelte +66 -0
- package/dist/components/docs/api/ApiResponse.svelte.d.ts +9 -0
- package/dist/components/docs/api/index.d.ts +5 -0
- package/dist/components/docs/api/index.js +5 -0
- package/dist/components/docs/componentTextProps.d.ts +3 -0
- package/dist/components/docs/componentTextProps.js +61 -0
- package/dist/components/docs/index.d.ts +54 -0
- package/dist/components/docs/index.js +56 -0
- package/dist/components/global/VersionNotFound.svelte +48 -0
- package/dist/components/global/VersionNotFound.svelte.d.ts +7 -0
- package/dist/components/global/index.d.ts +1 -0
- package/dist/components/global/index.js +1 -0
- package/dist/components/index.d.ts +6 -822
- package/dist/components/index.js +11 -3854
- package/dist/components/ui/Badge.svelte +48 -0
- package/dist/components/ui/Badge.svelte.d.ts +15 -0
- package/dist/components/ui/Button.svelte +58 -0
- package/dist/components/ui/Button.svelte.d.ts +17 -0
- package/dist/components/ui/Dialog.svelte +16 -0
- package/dist/components/ui/Dialog.svelte.d.ts +9 -0
- package/dist/components/ui/DialogClose.svelte +16 -0
- package/dist/components/ui/DialogClose.svelte.d.ts +9 -0
- package/dist/components/ui/DialogContent.svelte +43 -0
- package/dist/components/ui/DialogContent.svelte.d.ts +10 -0
- package/dist/components/ui/DialogDescription.svelte +21 -0
- package/dist/components/ui/DialogDescription.svelte.d.ts +9 -0
- package/dist/components/ui/DialogFooter.svelte +20 -0
- package/dist/components/ui/DialogFooter.svelte.d.ts +9 -0
- package/dist/components/ui/DialogHeader.svelte +20 -0
- package/dist/components/ui/DialogHeader.svelte.d.ts +9 -0
- package/dist/components/ui/DialogTitle.svelte +21 -0
- package/dist/components/ui/DialogTitle.svelte.d.ts +9 -0
- package/dist/components/ui/Input.svelte +23 -0
- package/dist/components/ui/Input.svelte.d.ts +8 -0
- package/dist/components/ui/Textarea.svelte +19 -0
- package/dist/components/ui/Textarea.svelte.d.ts +7 -0
- package/dist/components/ui/index.d.ts +11 -0
- package/dist/components/ui/index.js +11 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +9 -0
- package/dist/config.schema.json +471 -0
- package/dist/config.server.d.ts +46 -0
- package/dist/config.server.js +149 -0
- package/dist/{mdx-ColN3Cyg.d.mts → config.types.d.ts} +22 -75
- package/dist/config.types.js +39 -0
- package/dist/dev-utils.d.ts +29 -0
- package/dist/dev-utils.js +63 -0
- package/dist/index.d.ts +19 -4
- package/dist/index.js +25 -4861
- package/dist/mdx-cache.d.ts +41 -0
- package/dist/mdx-cache.js +160 -0
- package/dist/mdx-components.js +50 -1931
- package/dist/mdx-security.d.ts +76 -0
- package/dist/mdx-security.js +217 -0
- package/dist/mdx.d.ts +73 -0
- package/dist/mdx.js +1099 -0
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.js +2 -0
- package/dist/middleware/security.d.ts +22 -47
- package/dist/middleware/security.js +111 -137
- package/dist/parsers/base-parser.d.ts +14 -0
- package/dist/parsers/base-parser.js +1 -0
- package/dist/parsers/index.d.ts +16 -0
- package/dist/parsers/index.js +51 -0
- package/dist/parsers/openapi-parser.d.ts +18 -0
- package/dist/parsers/openapi-parser.js +209 -0
- package/dist/parsers/postman-parser.d.ts +20 -0
- package/dist/parsers/postman-parser.js +260 -0
- package/dist/parsers/specra-parser.d.ts +10 -0
- package/dist/parsers/specra-parser.js +18 -0
- package/dist/redirects.d.ts +12 -0
- package/dist/redirects.js +30 -0
- package/dist/remark-code-meta.d.ts +6 -0
- package/dist/remark-code-meta.js +21 -0
- package/dist/sidebar-utils.d.ts +59 -0
- package/dist/sidebar-utils.js +144 -0
- package/dist/stores/config.d.ts +20 -0
- package/dist/stores/config.js +45 -0
- package/dist/stores/index.d.ts +4 -0
- package/dist/stores/index.js +4 -0
- package/dist/stores/sidebar.d.ts +7 -0
- package/dist/stores/sidebar.js +12 -0
- package/dist/stores/tabs.d.ts +6 -0
- package/dist/stores/tabs.js +41 -0
- package/dist/stores/theme.d.ts +7 -0
- package/dist/stores/theme.js +75 -0
- package/dist/{styles.css → styles/globals.css} +136 -6
- package/dist/toc.d.ts +9 -0
- package/dist/toc.js +15 -0
- package/dist/utils.d.ts +13 -0
- package/dist/utils.js +30 -0
- package/package.json +47 -90
- package/dist/app/api/mdx-watch/route.d.mts +0 -10
- package/dist/app/api/mdx-watch/route.d.ts +0 -10
- package/dist/app/api/mdx-watch/route.js +0 -118
- package/dist/app/api/mdx-watch/route.js.map +0 -1
- package/dist/app/api/mdx-watch/route.mjs +0 -91
- package/dist/app/api/mdx-watch/route.mjs.map +0 -1
- package/dist/chunk-6S3EJVEO.mjs +0 -259
- package/dist/chunk-6S3EJVEO.mjs.map +0 -1
- package/dist/chunk-BE7EROIW.mjs +0 -212
- package/dist/chunk-BE7EROIW.mjs.map +0 -1
- package/dist/chunk-CWHRZHZO.mjs +0 -168
- package/dist/chunk-CWHRZHZO.mjs.map +0 -1
- package/dist/chunk-D5VDVYFY.mjs +0 -1325
- package/dist/chunk-D5VDVYFY.mjs.map +0 -1
- package/dist/chunk-WMCO2UX5.mjs +0 -585
- package/dist/chunk-WMCO2UX5.mjs.map +0 -1
- package/dist/chunk-XEMGCPZZ.mjs +0 -475
- package/dist/chunk-XEMGCPZZ.mjs.map +0 -1
- package/dist/components/index.d.mts +0 -822
- package/dist/components/index.js.map +0 -1
- package/dist/components/index.mjs +0 -3741
- package/dist/components/index.mjs.map +0 -1
- package/dist/index.d.mts +0 -4
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -1897
- package/dist/index.mjs.map +0 -1
- package/dist/layouts/index.d.mts +0 -34
- package/dist/layouts/index.d.ts +0 -34
- package/dist/layouts/index.js +0 -453
- package/dist/layouts/index.js.map +0 -1
- package/dist/layouts/index.mjs +0 -173
- package/dist/layouts/index.mjs.map +0 -1
- package/dist/lib/index.d.mts +0 -583
- package/dist/lib/index.d.ts +0 -583
- package/dist/lib/index.js +0 -1595
- package/dist/lib/index.js.map +0 -1
- package/dist/lib/index.mjs +0 -111
- package/dist/lib/index.mjs.map +0 -1
- package/dist/mdx-ColN3Cyg.d.ts +0 -352
- package/dist/mdx-components.d.mts +0 -86
- package/dist/mdx-components.d.ts +0 -86
- package/dist/mdx-components.js.map +0 -1
- package/dist/mdx-components.mjs +0 -206
- package/dist/mdx-components.mjs.map +0 -1
- package/dist/middleware/security.d.mts +0 -82
- package/dist/middleware/security.js.map +0 -1
- package/dist/middleware/security.mjs +0 -84
- package/dist/middleware/security.mjs.map +0 -1
- package/dist/styles.css.map +0 -1
- package/dist/styles.d.mts +0 -2
- package/dist/styles.d.ts +0 -2
- package/dist/styles.js +0 -2
- package/dist/styles.js.map +0 -1
- package/dist/styles.mjs +0 -1
- package/dist/styles.mjs.map +0 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for OpenAPI 3.0/3.1 specifications
|
|
3
|
+
*/
|
|
4
|
+
export class OpenApiParser {
|
|
5
|
+
validate(input) {
|
|
6
|
+
return (typeof input === "object" &&
|
|
7
|
+
input !== null &&
|
|
8
|
+
("openapi" in input || "swagger" in input) &&
|
|
9
|
+
"paths" in input);
|
|
10
|
+
}
|
|
11
|
+
parse(input) {
|
|
12
|
+
if (!this.validate(input)) {
|
|
13
|
+
throw new Error("Invalid OpenAPI spec format");
|
|
14
|
+
}
|
|
15
|
+
const baseUrl = this.extractBaseUrl(input);
|
|
16
|
+
const endpoints = [];
|
|
17
|
+
// Parse paths
|
|
18
|
+
for (const [path, pathItem] of Object.entries(input.paths || {})) {
|
|
19
|
+
const methods = ["get", "post", "put", "patch", "delete"];
|
|
20
|
+
for (const method of methods) {
|
|
21
|
+
const operation = pathItem[method];
|
|
22
|
+
if (!operation)
|
|
23
|
+
continue;
|
|
24
|
+
const endpoint = this.parseOperation(path, method.toUpperCase(), operation, input);
|
|
25
|
+
endpoints.push(endpoint);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
version: input.info?.version,
|
|
30
|
+
title: input.info?.title,
|
|
31
|
+
description: input.info?.description,
|
|
32
|
+
baseUrl,
|
|
33
|
+
auth: this.extractAuth(input),
|
|
34
|
+
endpoints,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
extractBaseUrl(spec) {
|
|
38
|
+
// OpenAPI 3.x servers
|
|
39
|
+
if (spec.servers && spec.servers.length > 0) {
|
|
40
|
+
return spec.servers[0].url;
|
|
41
|
+
}
|
|
42
|
+
// Swagger 2.0
|
|
43
|
+
if (spec.host) {
|
|
44
|
+
const scheme = spec.schemes?.[0] || "https";
|
|
45
|
+
const basePath = spec.basePath || "";
|
|
46
|
+
return `${scheme}://${spec.host}${basePath}`;
|
|
47
|
+
}
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
extractAuth(spec) {
|
|
51
|
+
const securitySchemes = spec.components?.securitySchemes || spec.securityDefinitions;
|
|
52
|
+
if (!securitySchemes)
|
|
53
|
+
return undefined;
|
|
54
|
+
// Get the first security scheme
|
|
55
|
+
const firstScheme = Object.values(securitySchemes)[0];
|
|
56
|
+
if (!firstScheme)
|
|
57
|
+
return undefined;
|
|
58
|
+
if (firstScheme.type === "http" && firstScheme.scheme === "bearer") {
|
|
59
|
+
return {
|
|
60
|
+
type: "bearer",
|
|
61
|
+
description: firstScheme.description,
|
|
62
|
+
tokenPrefix: "Bearer",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (firstScheme.type === "apiKey") {
|
|
66
|
+
return {
|
|
67
|
+
type: "apiKey",
|
|
68
|
+
description: firstScheme.description,
|
|
69
|
+
headerName: firstScheme.name || "X-API-Key",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (firstScheme.type === "http" && firstScheme.scheme === "basic") {
|
|
73
|
+
return {
|
|
74
|
+
type: "basic",
|
|
75
|
+
description: firstScheme.description,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
parseOperation(path, method, operation, spec) {
|
|
81
|
+
const endpoint = {
|
|
82
|
+
title: operation.summary || operation.operationId || `${method} ${path}`,
|
|
83
|
+
method,
|
|
84
|
+
path: this.convertPathParams(path),
|
|
85
|
+
description: operation.description,
|
|
86
|
+
};
|
|
87
|
+
// Parse parameters
|
|
88
|
+
const params = this.parseParameters(operation.parameters || [], spec);
|
|
89
|
+
if (params.path.length > 0)
|
|
90
|
+
endpoint.pathParams = params.path;
|
|
91
|
+
if (params.query.length > 0)
|
|
92
|
+
endpoint.queryParams = params.query;
|
|
93
|
+
if (params.header.length > 0) {
|
|
94
|
+
endpoint.headers = params.header.map((p) => ({
|
|
95
|
+
name: p.name,
|
|
96
|
+
value: p.example || "",
|
|
97
|
+
description: p.description,
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
// Parse request body
|
|
101
|
+
if (operation.requestBody) {
|
|
102
|
+
endpoint.body = this.parseRequestBody(operation.requestBody, spec);
|
|
103
|
+
}
|
|
104
|
+
// Parse responses
|
|
105
|
+
const responses = this.parseResponses(operation.responses || {}, spec);
|
|
106
|
+
if (responses.success)
|
|
107
|
+
endpoint.successResponse = responses.success;
|
|
108
|
+
if (responses.errors.length > 0)
|
|
109
|
+
endpoint.errorResponses = responses.errors;
|
|
110
|
+
return endpoint;
|
|
111
|
+
}
|
|
112
|
+
convertPathParams(path) {
|
|
113
|
+
// Convert OpenAPI {param} to :param
|
|
114
|
+
return path.replace(/\{([^}]+)\}/g, ":$1");
|
|
115
|
+
}
|
|
116
|
+
parseParameters(parameters, spec) {
|
|
117
|
+
const result = { path: [], query: [], header: [] };
|
|
118
|
+
for (const param of parameters) {
|
|
119
|
+
// Resolve $ref if present
|
|
120
|
+
const resolved = param.$ref ? this.resolveRef(param.$ref, spec) : param;
|
|
121
|
+
const apiParam = {
|
|
122
|
+
name: resolved.name,
|
|
123
|
+
type: resolved.schema?.type || resolved.type || "string",
|
|
124
|
+
required: resolved.required,
|
|
125
|
+
description: resolved.description,
|
|
126
|
+
example: resolved.example || resolved.schema?.example,
|
|
127
|
+
};
|
|
128
|
+
if (resolved.in === "path")
|
|
129
|
+
result.path.push(apiParam);
|
|
130
|
+
else if (resolved.in === "query")
|
|
131
|
+
result.query.push(apiParam);
|
|
132
|
+
else if (resolved.in === "header")
|
|
133
|
+
result.header.push(apiParam);
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
parseRequestBody(requestBody, spec) {
|
|
138
|
+
const content = requestBody.content?.["application/json"];
|
|
139
|
+
if (!content)
|
|
140
|
+
return undefined;
|
|
141
|
+
return {
|
|
142
|
+
description: requestBody.description,
|
|
143
|
+
example: content.example || this.generateExample(content.schema, spec),
|
|
144
|
+
schema: content.schema,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
parseResponses(responses, spec) {
|
|
148
|
+
const result = { errors: [] };
|
|
149
|
+
for (const [statusCode, response] of Object.entries(responses)) {
|
|
150
|
+
const status = parseInt(statusCode);
|
|
151
|
+
if (isNaN(status))
|
|
152
|
+
continue;
|
|
153
|
+
const resolved = response.$ref ? this.resolveRef(response.$ref, spec) : response;
|
|
154
|
+
const content = resolved.content?.["application/json"];
|
|
155
|
+
const apiResponse = {
|
|
156
|
+
status,
|
|
157
|
+
description: resolved.description,
|
|
158
|
+
example: content?.example || this.generateExample(content?.schema, spec),
|
|
159
|
+
schema: content?.schema,
|
|
160
|
+
};
|
|
161
|
+
if (status >= 200 && status < 300) {
|
|
162
|
+
result.success = apiResponse;
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
result.errors.push(apiResponse);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
generateExample(schema, spec) {
|
|
171
|
+
if (!schema)
|
|
172
|
+
return undefined;
|
|
173
|
+
if (schema.$ref)
|
|
174
|
+
schema = this.resolveRef(schema.$ref, spec);
|
|
175
|
+
if (schema.example)
|
|
176
|
+
return schema.example;
|
|
177
|
+
// Simple example generation based on schema type
|
|
178
|
+
if (schema.type === "object" && schema.properties) {
|
|
179
|
+
const example = {};
|
|
180
|
+
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
181
|
+
example[key] = this.generateExample(prop, spec);
|
|
182
|
+
}
|
|
183
|
+
return example;
|
|
184
|
+
}
|
|
185
|
+
if (schema.type === "array" && schema.items) {
|
|
186
|
+
return [this.generateExample(schema.items, spec)];
|
|
187
|
+
}
|
|
188
|
+
// Default values by type
|
|
189
|
+
const defaults = {
|
|
190
|
+
string: "string",
|
|
191
|
+
number: 0,
|
|
192
|
+
integer: 0,
|
|
193
|
+
boolean: false,
|
|
194
|
+
object: {},
|
|
195
|
+
array: [],
|
|
196
|
+
};
|
|
197
|
+
return defaults[schema.type] || null;
|
|
198
|
+
}
|
|
199
|
+
resolveRef(ref, spec) {
|
|
200
|
+
const path = ref.replace(/^#\//, "").split("/");
|
|
201
|
+
let current = spec;
|
|
202
|
+
for (const segment of path) {
|
|
203
|
+
current = current[segment];
|
|
204
|
+
if (!current)
|
|
205
|
+
return {};
|
|
206
|
+
}
|
|
207
|
+
return current;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SpecraApiSpec } from "../api-parser.types";
|
|
2
|
+
import type { ApiSpecParser } from "./base-parser";
|
|
3
|
+
/**
|
|
4
|
+
* Parser for Postman Collection v2.0/v2.1
|
|
5
|
+
*/
|
|
6
|
+
export declare class PostmanParser implements ApiSpecParser {
|
|
7
|
+
validate(input: any): boolean;
|
|
8
|
+
parse(input: any): SpecraApiSpec;
|
|
9
|
+
private extractBaseUrl;
|
|
10
|
+
private findFirstRequest;
|
|
11
|
+
private extractAuth;
|
|
12
|
+
private extractGlobalHeaders;
|
|
13
|
+
private parseItems;
|
|
14
|
+
private parseRequest;
|
|
15
|
+
private parseUrl;
|
|
16
|
+
private buildPath;
|
|
17
|
+
private parseUrlParams;
|
|
18
|
+
private parseBody;
|
|
19
|
+
private parseResponses;
|
|
20
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for Postman Collection v2.0/v2.1
|
|
3
|
+
*/
|
|
4
|
+
export class PostmanParser {
|
|
5
|
+
validate(input) {
|
|
6
|
+
return (typeof input === "object" &&
|
|
7
|
+
input !== null &&
|
|
8
|
+
"info" in input &&
|
|
9
|
+
input.info?.schema?.includes("v2"));
|
|
10
|
+
}
|
|
11
|
+
parse(input) {
|
|
12
|
+
if (!this.validate(input)) {
|
|
13
|
+
throw new Error("Invalid Postman Collection format (requires v2.0 or v2.1)");
|
|
14
|
+
}
|
|
15
|
+
const baseUrl = this.extractBaseUrl(input);
|
|
16
|
+
const endpoints = [];
|
|
17
|
+
// Parse items (can be nested in folders)
|
|
18
|
+
this.parseItems(input.item || [], endpoints, baseUrl, input);
|
|
19
|
+
return {
|
|
20
|
+
version: input.info?.version,
|
|
21
|
+
title: input.info?.name,
|
|
22
|
+
description: input.info?.description,
|
|
23
|
+
baseUrl,
|
|
24
|
+
auth: this.extractAuth(input.auth),
|
|
25
|
+
globalHeaders: this.extractGlobalHeaders(input),
|
|
26
|
+
endpoints,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
extractBaseUrl(collection) {
|
|
30
|
+
// Try to get from variables
|
|
31
|
+
const baseUrlVar = collection.variable?.find((v) => v.key === "baseUrl" || v.key === "base_url" || v.key === "url");
|
|
32
|
+
if (baseUrlVar)
|
|
33
|
+
return baseUrlVar.value;
|
|
34
|
+
// Try to extract from first request
|
|
35
|
+
if (collection.item && collection.item.length > 0) {
|
|
36
|
+
const firstRequest = this.findFirstRequest(collection.item);
|
|
37
|
+
if (firstRequest?.request?.url) {
|
|
38
|
+
const url = this.parseUrl(firstRequest.request.url);
|
|
39
|
+
if (url.host) {
|
|
40
|
+
return `${url.protocol}://${url.host.join(".")}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
findFirstRequest(items) {
|
|
47
|
+
for (const item of items) {
|
|
48
|
+
if (item.request)
|
|
49
|
+
return item;
|
|
50
|
+
if (item.item) {
|
|
51
|
+
const found = this.findFirstRequest(item.item);
|
|
52
|
+
if (found)
|
|
53
|
+
return found;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
extractAuth(auth) {
|
|
59
|
+
if (!auth)
|
|
60
|
+
return undefined;
|
|
61
|
+
if (auth.type === "bearer") {
|
|
62
|
+
return {
|
|
63
|
+
type: "bearer",
|
|
64
|
+
tokenPrefix: "Bearer",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (auth.type === "apikey") {
|
|
68
|
+
const keyData = auth.apikey?.find((a) => a.key === "key");
|
|
69
|
+
const keyName = keyData?.value || "X-API-Key";
|
|
70
|
+
return {
|
|
71
|
+
type: "apiKey",
|
|
72
|
+
headerName: keyName,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (auth.type === "basic") {
|
|
76
|
+
return {
|
|
77
|
+
type: "basic",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
extractGlobalHeaders(collection) {
|
|
83
|
+
// Postman doesn't have global headers in the same way, but we can check for common patterns
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
parseItems(items, endpoints, baseUrl, collection) {
|
|
87
|
+
for (const item of items) {
|
|
88
|
+
// If it's a folder, recurse
|
|
89
|
+
if (item.item && Array.isArray(item.item)) {
|
|
90
|
+
this.parseItems(item.item, endpoints, baseUrl, collection);
|
|
91
|
+
}
|
|
92
|
+
// If it's a request
|
|
93
|
+
else if (item.request) {
|
|
94
|
+
const endpoint = this.parseRequest(item, baseUrl, collection);
|
|
95
|
+
endpoints.push(endpoint);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
parseRequest(item, baseUrl, collection) {
|
|
100
|
+
const request = item.request;
|
|
101
|
+
const url = this.parseUrl(request.url);
|
|
102
|
+
const endpoint = {
|
|
103
|
+
title: item.name,
|
|
104
|
+
method: request.method.toUpperCase(),
|
|
105
|
+
path: this.buildPath(url, baseUrl),
|
|
106
|
+
description: item.request.description || item.description,
|
|
107
|
+
};
|
|
108
|
+
// Parse URL parameters (path and query)
|
|
109
|
+
const params = this.parseUrlParams(url);
|
|
110
|
+
if (params.path.length > 0)
|
|
111
|
+
endpoint.pathParams = params.path;
|
|
112
|
+
if (params.query.length > 0)
|
|
113
|
+
endpoint.queryParams = params.query;
|
|
114
|
+
// Parse headers
|
|
115
|
+
if (request.header && request.header.length > 0) {
|
|
116
|
+
endpoint.headers = request.header
|
|
117
|
+
.filter((h) => !h.disabled)
|
|
118
|
+
.map((h) => ({
|
|
119
|
+
name: h.key,
|
|
120
|
+
value: h.value || "",
|
|
121
|
+
description: h.description,
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
// Parse request body
|
|
125
|
+
if (request.body) {
|
|
126
|
+
endpoint.body = this.parseBody(request.body);
|
|
127
|
+
}
|
|
128
|
+
// Parse response examples
|
|
129
|
+
const responses = this.parseResponses(item.response || []);
|
|
130
|
+
if (responses.success)
|
|
131
|
+
endpoint.successResponse = responses.success;
|
|
132
|
+
if (responses.errors.length > 0)
|
|
133
|
+
endpoint.errorResponses = responses.errors;
|
|
134
|
+
return endpoint;
|
|
135
|
+
}
|
|
136
|
+
parseUrl(url) {
|
|
137
|
+
if (typeof url === "string") {
|
|
138
|
+
// Parse string URL
|
|
139
|
+
const urlObj = new URL(url);
|
|
140
|
+
return {
|
|
141
|
+
protocol: urlObj.protocol.replace(":", ""),
|
|
142
|
+
host: urlObj.hostname.split("."),
|
|
143
|
+
path: urlObj.pathname.split("/").filter(Boolean),
|
|
144
|
+
query: [],
|
|
145
|
+
variable: [],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
protocol: url.protocol || "https",
|
|
150
|
+
host: url.host || [],
|
|
151
|
+
path: url.path || [],
|
|
152
|
+
query: url.query || [],
|
|
153
|
+
variable: url.variable || [],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
buildPath(url, baseUrl) {
|
|
157
|
+
let path = "/";
|
|
158
|
+
if (url.path && url.path.length > 0) {
|
|
159
|
+
path += url.path.join("/");
|
|
160
|
+
}
|
|
161
|
+
// Convert Postman :param to our :param format (they're the same!)
|
|
162
|
+
// But we need to handle {{variable}} syntax
|
|
163
|
+
path = path.replace(/\{\{([^}]+)\}\}/g, ":$1");
|
|
164
|
+
return path;
|
|
165
|
+
}
|
|
166
|
+
parseUrlParams(url) {
|
|
167
|
+
const result = { path: [], query: [] };
|
|
168
|
+
// Path parameters from variables
|
|
169
|
+
if (url.variable && url.variable.length > 0) {
|
|
170
|
+
for (const v of url.variable) {
|
|
171
|
+
result.path.push({
|
|
172
|
+
name: v.key,
|
|
173
|
+
type: v.type || "string",
|
|
174
|
+
description: v.description,
|
|
175
|
+
example: v.value,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Extract path params from the path itself
|
|
180
|
+
if (url.path && url.path.length > 0) {
|
|
181
|
+
for (const segment of url.path) {
|
|
182
|
+
if (segment.startsWith(":")) {
|
|
183
|
+
const paramName = segment.slice(1);
|
|
184
|
+
// Only add if not already added from variables
|
|
185
|
+
if (!result.path.find((p) => p.name === paramName)) {
|
|
186
|
+
result.path.push({
|
|
187
|
+
name: paramName,
|
|
188
|
+
type: "string",
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Query parameters
|
|
195
|
+
if (url.query && url.query.length > 0) {
|
|
196
|
+
for (const q of url.query) {
|
|
197
|
+
if (q.disabled)
|
|
198
|
+
continue;
|
|
199
|
+
result.query.push({
|
|
200
|
+
name: q.key,
|
|
201
|
+
type: "string",
|
|
202
|
+
description: q.description,
|
|
203
|
+
example: q.value,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
parseBody(body) {
|
|
210
|
+
if (!body)
|
|
211
|
+
return undefined;
|
|
212
|
+
let example;
|
|
213
|
+
let description = body.description;
|
|
214
|
+
if (body.mode === "raw") {
|
|
215
|
+
try {
|
|
216
|
+
example = JSON.parse(body.raw);
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
example = body.raw;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
else if (body.mode === "formdata" || body.mode === "urlencoded") {
|
|
223
|
+
example = {};
|
|
224
|
+
for (const item of body[body.mode] || []) {
|
|
225
|
+
if (!item.disabled) {
|
|
226
|
+
example[item.key] = item.value;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
description,
|
|
232
|
+
example,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
parseResponses(responses) {
|
|
236
|
+
const result = { errors: [] };
|
|
237
|
+
for (const response of responses) {
|
|
238
|
+
let example;
|
|
239
|
+
try {
|
|
240
|
+
example = JSON.parse(response.body);
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
example = response.body;
|
|
244
|
+
}
|
|
245
|
+
const apiResponse = {
|
|
246
|
+
status: response.code || 200,
|
|
247
|
+
description: response.name,
|
|
248
|
+
example,
|
|
249
|
+
};
|
|
250
|
+
if (apiResponse.status >= 200 && apiResponse.status < 300) {
|
|
251
|
+
if (!result.success)
|
|
252
|
+
result.success = apiResponse;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
result.errors.push(apiResponse);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SpecraApiSpec } from "../api-parser.types";
|
|
2
|
+
import type { ApiSpecParser } from "./base-parser";
|
|
3
|
+
/**
|
|
4
|
+
* Parser for native Specra API format
|
|
5
|
+
* This is a pass-through parser since the input is already in the correct format
|
|
6
|
+
*/
|
|
7
|
+
export declare class SpecraParser implements ApiSpecParser {
|
|
8
|
+
validate(input: any): boolean;
|
|
9
|
+
parse(input: any): SpecraApiSpec;
|
|
10
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for native Specra API format
|
|
3
|
+
* This is a pass-through parser since the input is already in the correct format
|
|
4
|
+
*/
|
|
5
|
+
export class SpecraParser {
|
|
6
|
+
validate(input) {
|
|
7
|
+
return (typeof input === "object" &&
|
|
8
|
+
input !== null &&
|
|
9
|
+
"endpoints" in input &&
|
|
10
|
+
Array.isArray(input.endpoints));
|
|
11
|
+
}
|
|
12
|
+
parse(input) {
|
|
13
|
+
if (!this.validate(input)) {
|
|
14
|
+
throw new Error("Invalid Specra API spec format");
|
|
15
|
+
}
|
|
16
|
+
return input;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface RedirectMapping {
|
|
2
|
+
from: string;
|
|
3
|
+
to: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Build redirect mappings from all docs' redirect_from frontmatter
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildRedirectMappings(): Promise<RedirectMapping[]>;
|
|
9
|
+
/**
|
|
10
|
+
* Find redirect destination for a given path
|
|
11
|
+
*/
|
|
12
|
+
export declare function findRedirect(path: string): Promise<string | null>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getAllDocs, getVersions } from "./mdx";
|
|
2
|
+
/**
|
|
3
|
+
* Build redirect mappings from all docs' redirect_from frontmatter
|
|
4
|
+
*/
|
|
5
|
+
export async function buildRedirectMappings() {
|
|
6
|
+
const versions = getVersions();
|
|
7
|
+
const redirects = [];
|
|
8
|
+
for (const version of versions) {
|
|
9
|
+
const docs = await getAllDocs(version);
|
|
10
|
+
for (const doc of docs) {
|
|
11
|
+
if (doc.meta.redirect_from && Array.isArray(doc.meta.redirect_from)) {
|
|
12
|
+
for (const oldPath of doc.meta.redirect_from) {
|
|
13
|
+
redirects.push({
|
|
14
|
+
from: oldPath,
|
|
15
|
+
to: `/docs/${version}/${doc.slug}`,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return redirects;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Find redirect destination for a given path
|
|
25
|
+
*/
|
|
26
|
+
export async function findRedirect(path) {
|
|
27
|
+
const redirects = await buildRedirectMappings();
|
|
28
|
+
const redirect = redirects.find((r) => r.from === path);
|
|
29
|
+
return redirect ? redirect.to : null;
|
|
30
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remark plugin to extract code block meta strings and pass them as props
|
|
3
|
+
* Converts: ```js filename.js
|
|
4
|
+
* Into props: { language: 'js', meta: 'filename.js' }
|
|
5
|
+
*/
|
|
6
|
+
export function remarkCodeMeta() {
|
|
7
|
+
return (tree) => {
|
|
8
|
+
const visit = (node) => {
|
|
9
|
+
if (node.type === 'code' && node.meta) {
|
|
10
|
+
// Store the meta string in the node's data
|
|
11
|
+
node.data = node.data || {};
|
|
12
|
+
node.data.hProperties = node.data.hProperties || {};
|
|
13
|
+
node.data.hProperties.meta = node.meta;
|
|
14
|
+
}
|
|
15
|
+
if (node.children) {
|
|
16
|
+
node.children.forEach(visit);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
visit(tree);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified sidebar sorting and structure building utilities
|
|
3
|
+
* This module provides consistent sidebar logic across the application
|
|
4
|
+
* to ensure ordering is handled the same way everywhere.
|
|
5
|
+
*/
|
|
6
|
+
export interface SidebarGroup {
|
|
7
|
+
label: string;
|
|
8
|
+
path: string;
|
|
9
|
+
icon?: string;
|
|
10
|
+
items: any[];
|
|
11
|
+
position: number;
|
|
12
|
+
collapsible: boolean;
|
|
13
|
+
defaultCollapsed: boolean;
|
|
14
|
+
children: Record<string, SidebarGroup>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Sort sidebar items by their position.
|
|
18
|
+
* Items with explicit sidebar_position come first (sorted numerically),
|
|
19
|
+
* followed by items without position (sorted by their original order).
|
|
20
|
+
*
|
|
21
|
+
* @param items - Array of items with optional sidebar_position
|
|
22
|
+
* @returns Sorted array of items
|
|
23
|
+
*/
|
|
24
|
+
export declare function sortSidebarItems<T extends {
|
|
25
|
+
sidebar_position?: number;
|
|
26
|
+
meta?: any;
|
|
27
|
+
}>(items: T[]): T[];
|
|
28
|
+
/**
|
|
29
|
+
* Sort sidebar groups by their position.
|
|
30
|
+
* Groups with explicit position come first (sorted numerically),
|
|
31
|
+
* followed by groups without position at the end (sorted by their original order).
|
|
32
|
+
*
|
|
33
|
+
* @param groups - Record of group key to group object with position
|
|
34
|
+
* @returns Sorted array of [key, group] tuples
|
|
35
|
+
*/
|
|
36
|
+
export declare function sortSidebarGroups<T extends {
|
|
37
|
+
position: number;
|
|
38
|
+
}>(groups: Record<string, T>): [string, T][];
|
|
39
|
+
/**
|
|
40
|
+
* Build hierarchical sidebar structure from flat list of documents
|
|
41
|
+
* This is the single source of truth for sidebar structure used by both
|
|
42
|
+
* the sidebar component and navigation (prev/next) links.
|
|
43
|
+
*
|
|
44
|
+
* @param docs - Array of documents with metadata
|
|
45
|
+
* @returns Object containing root groups and standalone items
|
|
46
|
+
*/
|
|
47
|
+
export declare function buildSidebarStructure<T extends {
|
|
48
|
+
filePath: string;
|
|
49
|
+
slug: string;
|
|
50
|
+
categoryLabel?: string;
|
|
51
|
+
categoryPosition?: number;
|
|
52
|
+
categoryIcon?: string;
|
|
53
|
+
categoryCollapsible?: boolean;
|
|
54
|
+
categoryCollapsed?: boolean;
|
|
55
|
+
meta: any;
|
|
56
|
+
}>(docs: T[]): {
|
|
57
|
+
rootGroups: Record<string, SidebarGroup>;
|
|
58
|
+
standalone: T[];
|
|
59
|
+
};
|