nestjs-openapi 0.1.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/dist/index.mjs ADDED
@@ -0,0 +1,297 @@
1
+ export { c as categorizeBrokenRefs, d as defineConfig, f as findConfigFile, e as formatValidationResult, g as generate, b as loadAndResolveConfig, a as loadConfig, l as loadConfigFromFile, r as resolveConfig, v as validateSpec } from './shared/nestjs-openapi.DlNMM8Zq.mjs';
2
+ import { readFileSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import { Module } from '@nestjs/common';
5
+ export { generateAsync, generate as generateEffect } from './internal.mjs';
6
+ import { P as ProjectInitError, E as EntryNotFoundError } from './shared/nestjs-openapi.B1bBy_tG.mjs';
7
+ export { a as ConfigLoadError, C as ConfigNotFoundError, b as ConfigValidationError, I as InvalidMethodError, c as getAllControllers, q as getArrayInitializer, o as getControllerMethodInfos, e as getControllerName, d as getControllerPrefix, j as getControllerTags, h as getDecoratorName, k as getHttpDecorator, f as getHttpMethods, m as getMethodInfo, w as getModuleDecoratorArg, z as getModuleMetadata, g as getModules, s as getStringLiteralValue, u as getSymbolFromIdentifier, l as isHttpDecorator, i as isHttpMethod, v as isModuleClass, n as normalizePath, y as resolveArrayOfClasses, x as resolveClassFromExpression, r as resolveClassFromSymbol, t as transformMethod, p as transformMethods } from './shared/nestjs-openapi.B1bBy_tG.mjs';
8
+ import { Context, Effect, Layer } from 'effect';
9
+ import { Project } from 'ts-morph';
10
+ import 'glob';
11
+ import 'js-yaml';
12
+ import 'ts-json-schema-generator';
13
+ import 'node:crypto';
14
+ import 'node:url';
15
+ import 'child_process';
16
+
17
+ var __create = Object.create;
18
+ var __defProp = Object.defineProperty;
19
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
20
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
21
+ var __typeError = (msg) => {
22
+ throw TypeError(msg);
23
+ };
24
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
25
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
26
+ var __decoratorStart = (base) => [, , , __create(null)];
27
+ var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
28
+ var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
29
+ var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
30
+ var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
31
+ var __runInitializers = (array, flags, self, value) => {
32
+ for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) fns[i].call(self) ;
33
+ return value;
34
+ };
35
+ var __decorateElement = (array, flags, name, decorators, target, extra) => {
36
+ var it, done, ctx, k = flags & 7, p = false;
37
+ var j = 0;
38
+ var extraInitializers = array[j] || (array[j] = []);
39
+ var desc = k && ((target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(target , name));
40
+ __name(target, name);
41
+ for (var i = decorators.length - 1; i >= 0; i--) {
42
+ ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
43
+ it = (0, decorators[i])(target, ctx), done._ = 1;
44
+ __expectFn(it) && (target = it);
45
+ }
46
+ return __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
47
+ };
48
+ var _OpenApiModule_decorators, _init;
49
+ const PATH_METADATA = "path";
50
+ const METHOD_METADATA = "method";
51
+ const HEADERS_METADATA = "__headers__";
52
+ const CONTROLLER_WATERMARK = "__controller__";
53
+ const OPENAPI_MODULE_OPTIONS = Symbol("OPENAPI_MODULE_OPTIONS");
54
+ const OPENAPI_SPEC = Symbol("OPENAPI_SPEC");
55
+ function escapeHtml(text) {
56
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
57
+ }
58
+ function generateSwaggerUiHtml(title, jsonPath) {
59
+ return `<!DOCTYPE html>
60
+ <html lang="en">
61
+ <head>
62
+ <meta charset="UTF-8">
63
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
64
+ <title>${escapeHtml(title)}</title>
65
+ <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
66
+ <style>
67
+ html { box-sizing: border-box; overflow-y: scroll; }
68
+ *, *:before, *:after { box-sizing: inherit; }
69
+ body { margin: 0; background: #fafafa; }
70
+ </style>
71
+ </head>
72
+ <body>
73
+ <div id="swagger-ui"></div>
74
+ <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"><\/script>
75
+ <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js"><\/script>
76
+ <script>
77
+ window.onload = function() {
78
+ SwaggerUIBundle({
79
+ url: "${escapeHtml(jsonPath)}",
80
+ dom_id: '#swagger-ui',
81
+ deepLinking: true,
82
+ presets: [
83
+ SwaggerUIBundle.presets.apis,
84
+ SwaggerUIStandalonePreset
85
+ ],
86
+ plugins: [
87
+ SwaggerUIBundle.plugins.DownloadUrl
88
+ ],
89
+ layout: "StandaloneLayout"
90
+ });
91
+ };
92
+ <\/script>
93
+ </body>
94
+ </html>`;
95
+ }
96
+ function loadSpecFile(filePath) {
97
+ try {
98
+ const resolvedPath = resolve(process.cwd(), filePath);
99
+ const content = readFileSync(resolvedPath, "utf-8");
100
+ return JSON.parse(content);
101
+ } catch (error) {
102
+ if (error instanceof Error) {
103
+ if ("code" in error && error.code === "ENOENT") {
104
+ throw new Error(
105
+ `OpenAPI spec file not found: ${filePath}. Make sure to run 'nestjs-openapi generate' first.`
106
+ );
107
+ }
108
+ throw new Error(`Failed to load OpenAPI spec: ${error.message}`);
109
+ }
110
+ throw error;
111
+ }
112
+ }
113
+ function resolveOptions(options) {
114
+ let swaggerEnabled = false;
115
+ let swaggerPath = "/api-docs";
116
+ let swaggerTitle = "";
117
+ if (options.swagger === true) {
118
+ swaggerEnabled = true;
119
+ } else if (options.swagger && typeof options.swagger === "object") {
120
+ swaggerEnabled = true;
121
+ swaggerPath = options.swagger.path ?? "/api-docs";
122
+ swaggerTitle = options.swagger.title ?? "";
123
+ }
124
+ return {
125
+ specFile: options.specFile,
126
+ enabled: options.enabled ?? true,
127
+ jsonPath: options.jsonPath ?? "/openapi.json",
128
+ swagger: {
129
+ enabled: swaggerEnabled,
130
+ path: swaggerPath,
131
+ title: swaggerTitle
132
+ }
133
+ };
134
+ }
135
+ _OpenApiModule_decorators = [Module({})];
136
+ let _OpenApiModule = class _OpenApiModule {
137
+ /**
138
+ * Configure the OpenAPI module with options.
139
+ *
140
+ * @param options - Configuration options
141
+ * @returns Dynamic module configuration
142
+ */
143
+ static forRoot(options) {
144
+ const resolvedOptions = resolveOptions(options);
145
+ if (!resolvedOptions.enabled) {
146
+ return {
147
+ module: _OpenApiModule,
148
+ providers: [],
149
+ controllers: [],
150
+ exports: []
151
+ };
152
+ }
153
+ const spec = loadSpecFile(resolvedOptions.specFile);
154
+ const finalOptions = {
155
+ ...resolvedOptions,
156
+ swagger: {
157
+ ...resolvedOptions.swagger,
158
+ title: resolvedOptions.swagger.title || spec.info.title
159
+ }
160
+ };
161
+ const providers = [
162
+ {
163
+ provide: OPENAPI_MODULE_OPTIONS,
164
+ useValue: finalOptions
165
+ },
166
+ {
167
+ provide: OPENAPI_SPEC,
168
+ useValue: spec
169
+ }
170
+ ];
171
+ const controllers = createOpenApiControllers(finalOptions, spec);
172
+ return {
173
+ module: _OpenApiModule,
174
+ providers,
175
+ controllers,
176
+ exports: [OPENAPI_MODULE_OPTIONS, OPENAPI_SPEC]
177
+ };
178
+ }
179
+ };
180
+ _init = __decoratorStart();
181
+ _OpenApiModule = __decorateElement(_init, 0, "OpenApiModule", _OpenApiModule_decorators, _OpenApiModule);
182
+ __runInitializers(_init, 1, _OpenApiModule);
183
+ let OpenApiModule = _OpenApiModule;
184
+ function createJsonController(controllerPath, spec) {
185
+ class JsonSpecController {
186
+ getSpec() {
187
+ return spec;
188
+ }
189
+ }
190
+ Reflect.defineMetadata(CONTROLLER_WATERMARK, true, JsonSpecController);
191
+ Reflect.defineMetadata(PATH_METADATA, controllerPath, JsonSpecController);
192
+ const method = JsonSpecController.prototype.getSpec;
193
+ Reflect.defineMetadata(METHOD_METADATA, 0, method);
194
+ Reflect.defineMetadata(PATH_METADATA, "/", method);
195
+ Reflect.defineMetadata(
196
+ HEADERS_METADATA,
197
+ [{ name: "Content-Type", value: "application/json" }],
198
+ method
199
+ );
200
+ return JsonSpecController;
201
+ }
202
+ function createSwaggerUiController(controllerPath, title, jsonPath) {
203
+ class SwaggerUiController {
204
+ getSwaggerUi() {
205
+ return generateSwaggerUiHtml(title, jsonPath);
206
+ }
207
+ }
208
+ Reflect.defineMetadata(CONTROLLER_WATERMARK, true, SwaggerUiController);
209
+ Reflect.defineMetadata(PATH_METADATA, controllerPath, SwaggerUiController);
210
+ const method = SwaggerUiController.prototype.getSwaggerUi;
211
+ Reflect.defineMetadata(METHOD_METADATA, 0, method);
212
+ Reflect.defineMetadata(PATH_METADATA, "/", method);
213
+ Reflect.defineMetadata(
214
+ HEADERS_METADATA,
215
+ [{ name: "Content-Type", value: "text/html" }],
216
+ method
217
+ );
218
+ return SwaggerUiController;
219
+ }
220
+ function createOpenApiControllers(options, spec) {
221
+ const controllers = [];
222
+ const jsonController = createJsonController(options.jsonPath, spec);
223
+ controllers.push(jsonController);
224
+ if (options.swagger.enabled) {
225
+ const swaggerUiController = createSwaggerUiController(
226
+ options.swagger.path,
227
+ options.swagger.title,
228
+ options.jsonPath
229
+ );
230
+ controllers.push(swaggerUiController);
231
+ }
232
+ return controllers;
233
+ }
234
+
235
+ class ProjectService extends Context.Tag("ProjectService")() {
236
+ }
237
+ const initProject = Effect.fn("ProjectService.initProject")(function* (options) {
238
+ return yield* Effect.try({
239
+ try: () => new Project({
240
+ tsConfigFilePath: options.tsconfig,
241
+ skipAddingFilesFromTsConfig: true
242
+ }),
243
+ catch: (error) => ProjectInitError.create(options.tsconfig, error)
244
+ });
245
+ });
246
+ const addSourceFiles = Effect.fn("ProjectService.addSourceFiles")(function* (project, entry) {
247
+ return yield* Effect.try({
248
+ try: () => {
249
+ project.addSourceFilesAtPaths(entry);
250
+ project.resolveSourceFileDependencies();
251
+ },
252
+ catch: (error) => new ProjectInitError({
253
+ tsconfig: "",
254
+ message: `Failed to add source files: ${entry}`,
255
+ cause: error
256
+ })
257
+ });
258
+ });
259
+ const getEntrySourceFile = Effect.fn("ProjectService.getEntrySourceFile")(
260
+ function* (project, entry) {
261
+ const sourceFile = project.getSourceFile(entry);
262
+ if (!sourceFile) {
263
+ return yield* EntryNotFoundError.fileNotFound(entry);
264
+ }
265
+ return sourceFile;
266
+ }
267
+ );
268
+ const getEntryClass = Effect.fn("ProjectService.getEntryClass")(function* (sourceFile, entry, className = "AppModule") {
269
+ const entryClass = sourceFile.getClass(className);
270
+ if (!entryClass) {
271
+ return yield* EntryNotFoundError.classNotFound(entry, className);
272
+ }
273
+ return entryClass;
274
+ });
275
+ const makeProjectContext = Effect.fn(
276
+ "ProjectService.makeProjectContext"
277
+ )(function* (options) {
278
+ yield* Effect.logDebug("Initializing project").pipe(
279
+ Effect.annotateLogs({ entry: options.entry })
280
+ );
281
+ const project = yield* initProject(options);
282
+ yield* addSourceFiles(project, options.entry);
283
+ const entrySourceFile = yield* getEntrySourceFile(project, options.entry);
284
+ const entryClass = yield* getEntryClass(entrySourceFile, options.entry);
285
+ yield* Effect.logDebug("Project initialized successfully");
286
+ return {
287
+ project,
288
+ entrySourceFile,
289
+ entryClass
290
+ };
291
+ });
292
+ const ProjectServiceLive = (options) => Layer.effect(
293
+ ProjectService,
294
+ makeProjectContext(options).pipe(Effect.map((context) => ({ context })))
295
+ );
296
+
297
+ export { EntryNotFoundError, OPENAPI_MODULE_OPTIONS, OPENAPI_SPEC, OpenApiModule, ProjectInitError, ProjectService, ProjectServiceLive, generateSwaggerUiHtml, loadSpecFile, makeProjectContext, resolveOptions };
@@ -0,0 +1,2 @@
1
+ import 'effect';
2
+ export { G as GenerateOptions, g as generate, d as generateAsync } from './shared/nestjs-openapi.BYUrTaMo.mjs';
@@ -0,0 +1,2 @@
1
+ import 'effect';
2
+ export { G as GenerateOptions, g as generate, d as generateAsync } from './shared/nestjs-openapi.BYUrTaMo.js';
@@ -0,0 +1,53 @@
1
+ import { Effect } from 'effect';
2
+ import { Project } from 'ts-morph';
3
+ import { E as EntryNotFoundError, g as getModules, o as getControllerMethodInfos, p as transformMethods } from './shared/nestjs-openapi.B1bBy_tG.mjs';
4
+
5
+ const generate = (options) => Effect.gen(function* () {
6
+ yield* Effect.logInfo("Starting OpenAPI generation").pipe(
7
+ Effect.annotateLogs({ entry: options.entry })
8
+ );
9
+ const project = new Project({
10
+ tsConfigFilePath: options.tsconfig,
11
+ skipAddingFilesFromTsConfig: true
12
+ });
13
+ project.addSourceFilesAtPaths(options.entry);
14
+ project.resolveSourceFileDependencies();
15
+ const entrySourceFile = project.getSourceFile(options.entry);
16
+ if (!entrySourceFile) {
17
+ return yield* EntryNotFoundError.fileNotFound(options.entry);
18
+ }
19
+ const entryClass = entrySourceFile.getClass("AppModule");
20
+ if (!entryClass) {
21
+ return yield* EntryNotFoundError.classNotFound(
22
+ options.entry,
23
+ "AppModule"
24
+ );
25
+ }
26
+ const modules = yield* getModules(entryClass);
27
+ const methodInfos = modules.flatMap(
28
+ (mod) => mod.controllers.flatMap(
29
+ (controller) => getControllerMethodInfos(controller)
30
+ )
31
+ );
32
+ yield* Effect.logInfo("Collected method infos").pipe(
33
+ Effect.annotateLogs({
34
+ modules: modules.length,
35
+ methods: methodInfos.length
36
+ })
37
+ );
38
+ const paths = transformMethods(methodInfos);
39
+ yield* Effect.logInfo("OpenAPI generation complete").pipe(
40
+ Effect.annotateLogs({
41
+ paths: Object.keys(paths).length
42
+ })
43
+ );
44
+ return paths;
45
+ });
46
+ const generateAsync = async (options) => {
47
+ const program = generate(options).pipe(
48
+ Effect.mapError((error) => new Error(error.message))
49
+ );
50
+ return Effect.runPromise(program);
51
+ };
52
+
53
+ export { generate, generateAsync };