@wild-framework/openapi 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Wild Framework contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # @wild-framework/openapi
2
+
3
+ Auto-generated **OpenAPI 3.0 docs + Swagger UI** for [Wild Framework](https://github.com/KhandaSoftware/wild-framework) apps.
4
+
5
+ Generates an OpenAPI spec from your routes and Zod validators — **no hand-written spec files**. The docs always match your code.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @wild-framework/openapi
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ Register the provider **last** (after all feature providers) so it sees every route:
16
+
17
+ ```ts
18
+ import { createApplication } from "@wild-framework/core";
19
+ import { ContainerProvider } from "@wild-framework/container";
20
+ import { ConfigProvider } from "@wild-framework/config";
21
+ import { HttpProvider } from "@wild-framework/http";
22
+ import { OpenApiProvider } from "@wild-framework/openapi";
23
+
24
+ const app = createApplication();
25
+
26
+ app.register(ContainerProvider);
27
+ app.register(ConfigProvider);
28
+ app.register(HttpProvider);
29
+ app.register(UserProvider); // your routes
30
+
31
+ OpenApiProvider.options = { title: "My API", version: "1.0.0" };
32
+ app.register(OpenApiProvider); // last
33
+
34
+ await app.boot();
35
+ ```
36
+
37
+ Now available:
38
+
39
+ - **`GET /docs`** → interactive Swagger UI
40
+ - **`GET /openapi.json`** → the generated OpenAPI 3.0 spec
41
+
42
+ ## What it generates
43
+
44
+ Every route registered via the `Router` becomes an OpenAPI operation:
45
+
46
+ - Path: `/users/:id` → `/users/{id}`
47
+ - Request body: from your Zod validator classes (`CreateUserRequest` etc.)
48
+ - Operation ID: from the controller action (`UserController.index`)
49
+ - Bearer security scheme by default
50
+
51
+ See it live in [`examples/shop`](../../examples/shop) — run `pnpm example:shop` and open `http://localhost:3200/docs`.
52
+
53
+ ## Config
54
+
55
+ Set `OpenApiProvider.options` before registering:
56
+
57
+ | Option | Default | Description |
58
+ |--------|---------|-------------|
59
+ | `specPath` | `/openapi.json` | Where the JSON spec is served |
60
+ | `uiPath` | `/docs` | Where Swagger UI is served |
61
+ | `title` | `Wild Framework API` | Spec `info.title` |
62
+ | `version` | `1.0.0` | Spec `info.version` |
63
+ | `description` | — | Spec `info.description` |
64
+ | `bearerAuth` | `true` | Add a bearer security scheme |
65
+
66
+ ## Programmatic use
67
+
68
+ ```ts
69
+ import { generateOpenApi } from "@wild-framework/openapi";
70
+ import { Router } from "@wild-framework/http";
71
+
72
+ const spec = generateOpenApi(router, { title: "My API", version: "1.0.0" });
73
+ ```
@@ -0,0 +1,29 @@
1
+ import type { Router } from "@wild-framework/http";
2
+ /**
3
+ * Options for building an OpenAPI document.
4
+ */
5
+ export interface OpenApiOptions {
6
+ title: string;
7
+ version: string;
8
+ description?: string;
9
+ /** Whether to advertise a bearer security scheme. */
10
+ bearerAuth?: boolean;
11
+ }
12
+ /**
13
+ * A complete OpenAPI 3.0.x document (plain object, JSON-serializable).
14
+ */
15
+ export type OpenApiDocument = Record<string, unknown>;
16
+ /**
17
+ * Build an OpenAPI 3.0 document from a Router's registered routes.
18
+ *
19
+ * Every route's path, HTTP method, request validator (Zod → schema), and
20
+ * controller action are turned into OpenAPI operations. Responses are
21
+ * generic 200 + the framework's structured error shape by default.
22
+ */
23
+ export declare function generateOpenApi(router: Router, options: OpenApiOptions): OpenApiDocument;
24
+ /**
25
+ * Convert an Express-style path (`/users/:id`) to an OpenAPI path
26
+ * (`/users/{id}`).
27
+ */
28
+ export declare function normalizePath(path: string): string;
29
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAmB,MAAM,sBAAsB,CAAC;AAGpE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,eAAe,CA6CxF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD"}
@@ -0,0 +1,128 @@
1
+ import { zodSchemaToOpenApi } from "./zod-to-schema.js";
2
+ /**
3
+ * Build an OpenAPI 3.0 document from a Router's registered routes.
4
+ *
5
+ * Every route's path, HTTP method, request validator (Zod → schema), and
6
+ * controller action are turned into OpenAPI operations. Responses are
7
+ * generic 200 + the framework's structured error shape by default.
8
+ */
9
+ export function generateOpenApi(router, options) {
10
+ const paths = {};
11
+ const components = { schemas: {} };
12
+ const schemaNames = new Set();
13
+ // First pass: collect named schemas from validator classes so every
14
+ // validator ends up in components.schemas (and the frontend generator
15
+ // can produce real TS types).
16
+ for (const route of router.getRegisteredRoutes()) {
17
+ if (route.validator && route.validatorName) {
18
+ schemaNames.add(route.validatorName);
19
+ components.schemas[route.validatorName] =
20
+ zodSchemaToOpenApi(route.validator);
21
+ }
22
+ }
23
+ for (const route of router.getRegisteredRoutes()) {
24
+ const operation = buildOperation(route, schemaNames);
25
+ const pathKey = normalizePath(route.path);
26
+ if (!paths[pathKey]) {
27
+ paths[pathKey] = {};
28
+ }
29
+ paths[pathKey][route.method] = operation;
30
+ }
31
+ const doc = {
32
+ openapi: "3.0.3",
33
+ info: {
34
+ title: options.title,
35
+ version: options.version,
36
+ ...(options.description ? { description: options.description } : {}),
37
+ },
38
+ paths,
39
+ components,
40
+ };
41
+ if (options.bearerAuth !== false) {
42
+ doc.components.securitySchemes = {
43
+ bearerAuth: { type: "http", scheme: "bearer", bearerFormat: "JWT" },
44
+ };
45
+ doc.security = [{ bearerAuth: [] }];
46
+ }
47
+ return doc;
48
+ }
49
+ /**
50
+ * Convert an Express-style path (`/users/:id`) to an OpenAPI path
51
+ * (`/users/{id}`).
52
+ */
53
+ export function normalizePath(path) {
54
+ return path.replace(/:([A-Za-z0-9_]+)/g, "{$1}");
55
+ }
56
+ function buildOperation(route, schemaNames) {
57
+ const operation = {
58
+ responses: {
59
+ "200": { description: "OK" },
60
+ "400": {
61
+ description: "Bad Request",
62
+ content: {
63
+ "application/json": {
64
+ schema: errorSchema(),
65
+ },
66
+ },
67
+ },
68
+ "404": {
69
+ description: "Not Found",
70
+ content: {
71
+ "application/json": {
72
+ schema: errorSchema(),
73
+ },
74
+ },
75
+ },
76
+ "422": {
77
+ description: "Validation failed",
78
+ content: {
79
+ "application/json": {
80
+ schema: errorSchema(),
81
+ },
82
+ },
83
+ },
84
+ },
85
+ };
86
+ // OperationId from controller action, e.g. "UserController.index"
87
+ if (route.action) {
88
+ const ctor = route.action.controller;
89
+ operation.operationId = `${ctor.name ?? "Controller"}.${route.action.method}`;
90
+ operation.summary = `${route.method.toUpperCase()} ${route.path}`;
91
+ }
92
+ // Request body from the validator schema — reference the named component
93
+ // schema when one exists so frontend generators can type it.
94
+ if (route.validator) {
95
+ if (route.validatorName && schemaNames.has(route.validatorName)) {
96
+ operation.requestBody = {
97
+ required: true,
98
+ content: {
99
+ "application/json": {
100
+ schema: { $ref: `#/components/schemas/${route.validatorName}` },
101
+ },
102
+ },
103
+ };
104
+ }
105
+ else {
106
+ const schema = zodSchemaToOpenApi(route.validator);
107
+ operation.requestBody = {
108
+ required: true,
109
+ content: {
110
+ "application/json": { schema },
111
+ },
112
+ };
113
+ }
114
+ }
115
+ return operation;
116
+ }
117
+ function errorSchema() {
118
+ return {
119
+ type: "object",
120
+ properties: {
121
+ error: { type: "string" },
122
+ message: { type: "string" },
123
+ statusCode: { type: "integer" },
124
+ },
125
+ required: ["error", "message", "statusCode"],
126
+ };
127
+ }
128
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { OpenApiProvider, type OpenApiProviderOptions } from "./provider.js";
2
+ export { generateOpenApi, normalizePath, type OpenApiDocument, type OpenApiOptions, } from "./generator.js";
3
+ export { zodSchemaToOpenApi } from "./zod-to-schema.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EACH,eAAe,EACf,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,cAAc,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { OpenApiProvider } from "./provider.js";
2
+ export { generateOpenApi, normalizePath, } from "./generator.js";
3
+ export { zodSchemaToOpenApi } from "./zod-to-schema.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAA+B,MAAM,eAAe,CAAC;AAC7E,OAAO,EACH,eAAe,EACf,aAAa,GAGhB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { Provider, Application } from "@wild-framework/core";
2
+ /**
3
+ * OpenApiProvider options.
4
+ */
5
+ export interface OpenApiProviderOptions {
6
+ /** Path where the generated JSON spec is served. Default: "/openapi.json". */
7
+ specPath?: string;
8
+ /** Path where Swagger UI is served. Default: "/docs". */
9
+ uiPath?: string;
10
+ title?: string;
11
+ version?: string;
12
+ description?: string;
13
+ bearerAuth?: boolean;
14
+ }
15
+ /**
16
+ * Registers OpenAPI endpoints on the app's Router.
17
+ *
18
+ * - `register()`: nothing (routes must be registered before boot)
19
+ * - `boot()`: generates the spec from the Router's registered routes,
20
+ * then serves `GET /openapi.json` and `GET /docs` (Swagger UI)
21
+ *
22
+ * Register this provider LAST, after all feature providers.
23
+ *
24
+ * Configure via the static `options` before registering (the framework
25
+ * instantiates providers with a no-arg constructor):
26
+ *
27
+ * ```ts
28
+ * OpenApiProvider.options = { title: "Shop API", version: "1.0.0" };
29
+ * app.register(OpenApiProvider);
30
+ * ```
31
+ */
32
+ export declare class OpenApiProvider implements Provider {
33
+ /** Static configuration — set before `app.register(OpenApiProvider)`. */
34
+ static options: OpenApiProviderOptions;
35
+ boot(app: Application): void;
36
+ }
37
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAKlE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAgCD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,eAAgB,YAAW,QAAQ;IAC5C,yEAAyE;IACzE,MAAM,CAAC,OAAO,EAAE,sBAAsB,CAAM;IAE5C,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI;CA2B/B"}
@@ -0,0 +1,76 @@
1
+ import { Container } from "@wild-framework/container";
2
+ import { Router } from "@wild-framework/http";
3
+ import { generateOpenApi } from "./generator.js";
4
+ /**
5
+ * A minimal Swagger UI HTML page that loads the spec from `specPath`.
6
+ *
7
+ * Uses the CDN-hosted swagger-ui-dist assets — no bundling needed.
8
+ */
9
+ function swaggerUiHtml(specPath) {
10
+ return `<!DOCTYPE html>
11
+ <html lang="en">
12
+ <head>
13
+ <meta charset="utf-8" />
14
+ <title>API Docs</title>
15
+ <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
16
+ </head>
17
+ <body>
18
+ <div id="swagger-ui"></div>
19
+ <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
20
+ <script>
21
+ window.onload = () => {
22
+ window.ui = SwaggerUIBundle({
23
+ url: ${JSON.stringify(specPath)},
24
+ dom_id: '#swagger-ui',
25
+ deepLinking: true,
26
+ presets: [SwaggerUIBundle.presets.apis],
27
+ });
28
+ };
29
+ </script>
30
+ </body>
31
+ </html>`;
32
+ }
33
+ /**
34
+ * Registers OpenAPI endpoints on the app's Router.
35
+ *
36
+ * - `register()`: nothing (routes must be registered before boot)
37
+ * - `boot()`: generates the spec from the Router's registered routes,
38
+ * then serves `GET /openapi.json` and `GET /docs` (Swagger UI)
39
+ *
40
+ * Register this provider LAST, after all feature providers.
41
+ *
42
+ * Configure via the static `options` before registering (the framework
43
+ * instantiates providers with a no-arg constructor):
44
+ *
45
+ * ```ts
46
+ * OpenApiProvider.options = { title: "Shop API", version: "1.0.0" };
47
+ * app.register(OpenApiProvider);
48
+ * ```
49
+ */
50
+ export class OpenApiProvider {
51
+ /** Static configuration — set before `app.register(OpenApiProvider)`. */
52
+ static options = {};
53
+ boot(app) {
54
+ const container = app.resolve(Container);
55
+ const router = container.resolve(Router);
56
+ const options = OpenApiProvider.options;
57
+ const specPath = options.specPath ?? "/openapi.json";
58
+ const uiPath = options.uiPath ?? "/docs";
59
+ const opts = {
60
+ title: options.title ?? "Wild Framework API",
61
+ version: options.version ?? "1.0.0",
62
+ description: options.description,
63
+ bearerAuth: options.bearerAuth,
64
+ };
65
+ const spec = generateOpenApi(router, opts);
66
+ // Serve the JSON spec
67
+ router.get(specPath, (_req, res) => {
68
+ res.json(spec);
69
+ });
70
+ // Serve Swagger UI
71
+ router.get(uiPath, (_req, res) => {
72
+ res.type("html").send(swaggerUiHtml(specPath));
73
+ });
74
+ }
75
+ }
76
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAA6C,MAAM,gBAAgB,CAAC;AAgB5F;;;;GAIG;AACH,SAAS,aAAa,CAAC,QAAgB;IACnC,OAAO;;;;;;;;;;;;;eAaI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;;;;;;;;QAQ/B,CAAC;AACT,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,eAAe;IACxB,yEAAyE;IACzE,MAAM,CAAC,OAAO,GAA2B,EAAE,CAAC;IAE5C,IAAI,CAAC,GAAgB;QACjB,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAY,SAAS,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAS,MAAM,CAAC,CAAC;QAEjD,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC;QACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,eAAe,CAAC;QACrD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC;QAEzC,MAAM,IAAI,GAAmB;YACzB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,oBAAoB;YAC5C,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,OAAO;YACnC,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,UAAU,EAAE,OAAO,CAAC,UAAU;SACjC,CAAC;QAEF,MAAM,IAAI,GAAoB,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAE5D,sBAAsB;QACtB,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC/B,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC,CAAC,CAAC;QAEH,mBAAmB;QACnB,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC7B,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACP,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { ZodSchema } from "zod";
2
+ /**
3
+ * Convert a Zod schema to an OpenAPI/JSON Schema object.
4
+ *
5
+ * Uses `zod-to-json-schema` (mature library — per the "don't reinvent"
6
+ * principle). The result is suitable for OpenAPI `schema` fields.
7
+ */
8
+ export declare function zodSchemaToOpenApi(schema: ZodSchema, name?: string): Record<string, unknown>;
9
+ //# sourceMappingURL=zod-to-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-to-schema.d.ts","sourceRoot":"","sources":["../src/zod-to-schema.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAGrC;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqB5F"}
@@ -0,0 +1,26 @@
1
+ import { zodToJsonSchema } from "zod-to-json-schema";
2
+ /**
3
+ * Convert a Zod schema to an OpenAPI/JSON Schema object.
4
+ *
5
+ * Uses `zod-to-json-schema` (mature library — per the "don't reinvent"
6
+ * principle). The result is suitable for OpenAPI `schema` fields.
7
+ */
8
+ export function zodSchemaToOpenApi(schema, name) {
9
+ const resolvedName = name ?? "schema";
10
+ const json = zodToJsonSchema(schema, resolvedName);
11
+ // zod-to-json-schema wraps in { $schema, definitions, $ref }. Unwrap the
12
+ // top-level definition for a clean inline schema.
13
+ if (resolvedName && typeof json === "object" && json !== null) {
14
+ const defs = json.definitions;
15
+ const ref = json.$ref;
16
+ if (defs && ref) {
17
+ const key = ref.replace("#/definitions/", "");
18
+ const def = defs[key];
19
+ if (def && typeof def === "object") {
20
+ return def;
21
+ }
22
+ }
23
+ }
24
+ return json;
25
+ }
26
+ //# sourceMappingURL=zod-to-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-to-schema.js","sourceRoot":"","sources":["../src/zod-to-schema.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAiB,EAAE,IAAa;IAC/D,MAAM,YAAY,GAAG,IAAI,IAAI,QAAQ,CAAC;IACtC,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAEnD,yEAAyE;IACzE,kDAAkD;IAClD,IAAI,YAAY,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC5D,MAAM,IAAI,GAAI,IAAgC,CAAC,WAEhC,CAAC;QAChB,MAAM,GAAG,GAAI,IAAgC,CAAC,IAA0B,CAAC;QACzE,IAAI,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;YAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBACjC,OAAO,GAA8B,CAAC;YAC1C,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO,IAA+B,CAAC;AAC3C,CAAC"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@wild-framework/openapi",
3
+ "version": "0.2.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "license": "MIT",
20
+ "author": "Sphah",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/KhandaSoftware/wild-framework.git"
24
+ },
25
+ "description": "Auto-generated OpenAPI 3.0 docs and Swagger UI for Wild Framework apps.",
26
+ "keywords": [
27
+ "wild",
28
+ "framework",
29
+ "openapi",
30
+ "swagger",
31
+ "docs",
32
+ "api"
33
+ ],
34
+ "dependencies": {
35
+ "zod": "^3.24.0",
36
+ "zod-to-json-schema": "^3.24.0",
37
+ "@wild-framework/core": "0.2.0",
38
+ "@wild-framework/container": "0.2.0",
39
+ "@wild-framework/http": "0.2.0",
40
+ "@wild-framework/config": "0.2.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/express": "^5.0.0",
44
+ "express": "^5.1.0"
45
+ },
46
+ "scripts": {
47
+ "build": "tsc",
48
+ "clean": "rm -rf dist",
49
+ "test": "vitest run"
50
+ }
51
+ }