@navios/openapi-fastify 0.7.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.
Files changed (42) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/LICENSE +8 -0
  3. package/README.md +311 -0
  4. package/dist/src/index.d.mts +6 -0
  5. package/dist/src/index.d.mts.map +1 -0
  6. package/dist/src/openapi-fastify.plugin.d.mts +74 -0
  7. package/dist/src/openapi-fastify.plugin.d.mts.map +1 -0
  8. package/dist/src/schemas/index.d.mts +2 -0
  9. package/dist/src/schemas/index.d.mts.map +1 -0
  10. package/dist/src/schemas/openapi-fastify-options.schema.d.mts +113 -0
  11. package/dist/src/schemas/openapi-fastify-options.schema.d.mts.map +1 -0
  12. package/dist/src/utils/apply-global-prefix.util.d.mts +26 -0
  13. package/dist/src/utils/apply-global-prefix.util.d.mts.map +1 -0
  14. package/dist/src/utils/index.d.mts +2 -0
  15. package/dist/src/utils/index.d.mts.map +1 -0
  16. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  17. package/dist/tsconfig.tsbuildinfo +1 -0
  18. package/dist/tsdown.config.d.mts +3 -0
  19. package/dist/tsdown.config.d.mts.map +1 -0
  20. package/dist/vitest.config.d.mts +3 -0
  21. package/dist/vitest.config.d.mts.map +1 -0
  22. package/lib/index.cjs +233 -0
  23. package/lib/index.cjs.map +1 -0
  24. package/lib/index.d.cts +214 -0
  25. package/lib/index.d.cts.map +1 -0
  26. package/lib/index.d.mts +214 -0
  27. package/lib/index.d.mts.map +1 -0
  28. package/lib/index.mjs +185 -0
  29. package/lib/index.mjs.map +1 -0
  30. package/package.json +48 -0
  31. package/project.json +66 -0
  32. package/src/index.mts +41 -0
  33. package/src/openapi-fastify.plugin.mts +167 -0
  34. package/src/schemas/index.mts +10 -0
  35. package/src/schemas/openapi-fastify-options.schema.mts +129 -0
  36. package/src/utils/apply-global-prefix.util.mts +52 -0
  37. package/src/utils/index.mts +1 -0
  38. package/tsconfig.json +14 -0
  39. package/tsconfig.lib.json +8 -0
  40. package/tsconfig.spec.json +7 -0
  41. package/tsdown.config.mts +35 -0
  42. package/vitest.config.mts +11 -0
@@ -0,0 +1,167 @@
1
+ import type {
2
+ NaviosPlugin,
3
+ PluginContext,
4
+ PluginDefinition,
5
+ } from '@navios/core'
6
+ import type { OpenApiGeneratorOptions } from '@navios/openapi'
7
+ import type { FastifyInstance } from 'fastify'
8
+
9
+ import { OpenApiGeneratorService } from '@navios/openapi'
10
+
11
+ import { getHtmlDocument } from '@scalar/core/libs/html-rendering'
12
+ import { stringify as yamlStringify } from 'yaml'
13
+
14
+ import type {
15
+ FastifyOpenApiPluginOptionsBase,
16
+ ScalarOptions,
17
+ ScalarTheme,
18
+ } from './schemas/index.mjs'
19
+
20
+ import { fastifyOpenApiPluginOptionsSchema } from './schemas/index.mjs'
21
+ import { applyGlobalPrefix } from './utils/index.mjs'
22
+
23
+ /**
24
+ * Combined options for the Fastify OpenAPI plugin.
25
+ * Extends OpenApiGeneratorOptions with Fastify-specific settings.
26
+ */
27
+ export interface FastifyOpenApiPluginOptions
28
+ extends OpenApiGeneratorOptions, Partial<FastifyOpenApiPluginOptionsBase> {}
29
+
30
+ /**
31
+ * Class-based OpenAPI Fastify plugin that integrates with Navios plugin system.
32
+ *
33
+ * This plugin:
34
+ * - Scans all registered modules for endpoints
35
+ * - Generates an OpenAPI 3.1 document
36
+ * - Serves the document as JSON and optionally YAML
37
+ * - Provides Scalar UI for interactive documentation
38
+ */
39
+ export class OpenApiFastifyPlugin implements NaviosPlugin<FastifyOpenApiPluginOptions> {
40
+ readonly name = 'openapi-fastify'
41
+
42
+ async register(
43
+ context: PluginContext,
44
+ options: FastifyOpenApiPluginOptions,
45
+ ): Promise<void> {
46
+ const fastify = context.server as FastifyInstance
47
+
48
+ // Parse and validate options with defaults
49
+ const parsedOptions = fastifyOpenApiPluginOptionsSchema.parse(options)
50
+
51
+ // Get the generator service from the container
52
+ const generator = await context.container.get(OpenApiGeneratorService)
53
+
54
+ // Generate OpenAPI document from discovered endpoints
55
+ const document = generator.generate(context.modules, options)
56
+
57
+ // Apply global prefix to servers if not already set
58
+ const documentWithServers = applyGlobalPrefix(
59
+ document,
60
+ context.globalPrefix,
61
+ options,
62
+ )
63
+
64
+ // Register JSON endpoint
65
+ const jsonPath = parsedOptions.jsonPath
66
+ fastify.get(jsonPath, async (_request, reply) => {
67
+ return reply.send(documentWithServers)
68
+ })
69
+
70
+ // Register YAML endpoint (disabled by default)
71
+ if (!parsedOptions.disableYaml) {
72
+ const yamlPath = parsedOptions.yamlPath
73
+ fastify.get(yamlPath, async (_request, reply) => {
74
+ reply.type('text/yaml')
75
+ return reply.send(yamlStringify(documentWithServers))
76
+ })
77
+ }
78
+
79
+ // Register Scalar UI (unless disabled)
80
+ if (!parsedOptions.disableScalar) {
81
+ const docsPath = parsedOptions.docsPath
82
+ const scalarOptions = options.scalar ?? {}
83
+
84
+ // Generate HTML document using @scalar/core
85
+ const html = this.generateScalarHtml(jsonPath, scalarOptions)
86
+
87
+ fastify.get(docsPath, async (_request, reply) => {
88
+ reply.type('text/html')
89
+ return reply.send(html)
90
+ })
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Generates the Scalar API Reference HTML document.
96
+ *
97
+ * @param specUrl - URL to the OpenAPI JSON specification
98
+ * @param options - Scalar UI configuration options
99
+ * @returns Complete HTML document string
100
+ */
101
+ private generateScalarHtml(specUrl: string, options: ScalarOptions): string {
102
+ return getHtmlDocument({
103
+ url: specUrl,
104
+ theme: options.theme ?? 'default',
105
+ favicon: options.favicon,
106
+ customCss: options.customCss,
107
+ hideDownloadButton: options.hideDownloadButton,
108
+ hideSearch: options.hideSearch,
109
+ metaData: options.metaData,
110
+ cdn: options.cdn,
111
+ pageTitle: options.metaData?.title ?? 'API Reference',
112
+ })
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Creates a plugin definition for the OpenAPI Fastify plugin.
118
+ *
119
+ * @param options - Plugin configuration options
120
+ * @returns Plugin definition to pass to `app.usePlugin()`
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * import { NaviosFactory } from '@navios/core'
125
+ * import { defineFastifyEnvironment } from '@navios/adapter-fastify'
126
+ * import { defineOpenApiPlugin } from '@navios/openapi-fastify'
127
+ *
128
+ * const app = await NaviosFactory.create(AppModule, {
129
+ * adapter: defineFastifyEnvironment(),
130
+ * })
131
+ *
132
+ * app.usePlugin(defineOpenApiPlugin({
133
+ * info: {
134
+ * title: 'My API',
135
+ * version: '1.0.0',
136
+ * description: 'API documentation',
137
+ * },
138
+ * servers: [
139
+ * { url: 'http://localhost:3000', description: 'Development' },
140
+ * ],
141
+ * securitySchemes: {
142
+ * bearerAuth: {
143
+ * type: 'http',
144
+ * scheme: 'bearer',
145
+ * bearerFormat: 'JWT',
146
+ * },
147
+ * },
148
+ * scalar: {
149
+ * theme: 'purple',
150
+ * },
151
+ * }))
152
+ *
153
+ * await app.listen({ port: 3000 })
154
+ * // API docs available at http://localhost:3000/docs
155
+ * ```
156
+ */
157
+ export function defineOpenApiPlugin(
158
+ options: FastifyOpenApiPluginOptions,
159
+ ): PluginDefinition<FastifyOpenApiPluginOptions> {
160
+ return {
161
+ plugin: new OpenApiFastifyPlugin(),
162
+ options,
163
+ }
164
+ }
165
+
166
+ // Re-export types for convenience
167
+ export type { ScalarOptions, ScalarTheme }
@@ -0,0 +1,10 @@
1
+ export {
2
+ fastifyOpenApiPluginOptionsSchema,
3
+ scalarMetaDataSchema,
4
+ scalarOptionsSchema,
5
+ scalarThemeSchema,
6
+ type FastifyOpenApiPluginOptionsBase,
7
+ type ScalarMetaData,
8
+ type ScalarOptions,
9
+ type ScalarTheme,
10
+ } from './openapi-fastify-options.schema.mjs'
@@ -0,0 +1,129 @@
1
+ import { z } from 'zod'
2
+
3
+ /**
4
+ * Zod schema for Scalar UI theme options
5
+ */
6
+ export const scalarThemeSchema = z.enum([
7
+ 'default',
8
+ 'alternate',
9
+ 'moon',
10
+ 'purple',
11
+ 'solarized',
12
+ 'bluePlanet',
13
+ 'saturn',
14
+ 'kepler',
15
+ 'mars',
16
+ 'deepSpace',
17
+ 'laserwave',
18
+ 'elysiajs',
19
+ 'fastify',
20
+ 'none',
21
+ ])
22
+
23
+ /**
24
+ * Zod schema for Scalar UI metadata
25
+ */
26
+ export const scalarMetaDataSchema = z
27
+ .object({
28
+ title: z.string().optional(),
29
+ description: z.string().optional(),
30
+ ogDescription: z.string().optional(),
31
+ ogTitle: z.string().optional(),
32
+ ogImage: z.string().optional(),
33
+ twitterCard: z.string().optional(),
34
+ })
35
+ .optional()
36
+
37
+ /**
38
+ * Zod schema for Scalar UI configuration options
39
+ */
40
+ export const scalarOptionsSchema = z.object({
41
+ /**
42
+ * Theme for Scalar UI
43
+ * @default 'default'
44
+ */
45
+ theme: scalarThemeSchema.optional(),
46
+
47
+ /**
48
+ * Custom favicon URL
49
+ */
50
+ favicon: z.string().optional(),
51
+
52
+ /**
53
+ * Custom logo URL
54
+ */
55
+ logo: z.string().optional(),
56
+
57
+ /**
58
+ * Hide the "Download OpenAPI Spec" button
59
+ * @default false
60
+ */
61
+ hideDownloadButton: z.boolean().optional(),
62
+
63
+ /**
64
+ * Hide the "Search" input
65
+ * @default false
66
+ */
67
+ hideSearch: z.boolean().optional(),
68
+
69
+ /**
70
+ * Custom CSS to inject
71
+ */
72
+ customCss: z.string().optional(),
73
+
74
+ /**
75
+ * Meta data for the HTML page
76
+ */
77
+ metaData: scalarMetaDataSchema,
78
+
79
+ /**
80
+ * CDN URL for Scalar API Reference
81
+ * @default 'https://cdn.jsdelivr.net/npm/@scalar/api-reference'
82
+ */
83
+ cdn: z.string().optional(),
84
+ })
85
+
86
+ /**
87
+ * Zod schema for Fastify OpenAPI plugin options
88
+ */
89
+ export const fastifyOpenApiPluginOptionsSchema = z.object({
90
+ /**
91
+ * Path to serve OpenAPI JSON
92
+ * @default '/openapi.json'
93
+ */
94
+ jsonPath: z.string().optional().default('/openapi.json'),
95
+
96
+ /**
97
+ * Path to serve OpenAPI YAML
98
+ * @default '/openapi.yaml'
99
+ */
100
+ yamlPath: z.string().optional().default('/openapi.yaml'),
101
+
102
+ /**
103
+ * Path to serve Scalar UI
104
+ * @default '/docs'
105
+ */
106
+ docsPath: z.string().optional().default('/docs'),
107
+
108
+ /**
109
+ * Scalar UI configuration
110
+ */
111
+ scalar: scalarOptionsSchema.optional(),
112
+
113
+ /**
114
+ * Disable Scalar UI (only serve JSON/YAML)
115
+ * @default false
116
+ */
117
+ disableScalar: z.boolean().optional().default(false),
118
+
119
+ /**
120
+ * Disable YAML endpoint
121
+ * @default true
122
+ */
123
+ disableYaml: z.boolean().optional().default(true),
124
+ })
125
+
126
+ export type ScalarTheme = z.infer<typeof scalarThemeSchema>
127
+ export type ScalarMetaData = z.infer<typeof scalarMetaDataSchema>
128
+ export type ScalarOptions = z.infer<typeof scalarOptionsSchema>
129
+ export type FastifyOpenApiPluginOptionsBase = z.infer<typeof fastifyOpenApiPluginOptionsSchema>
@@ -0,0 +1,52 @@
1
+ import type { OpenApiGeneratorOptions } from '@navios/openapi'
2
+
3
+ /**
4
+ * OpenAPI document shape (minimal interface for typing)
5
+ */
6
+ export interface OpenAPIDocument {
7
+ openapi: string
8
+ info: {
9
+ title: string
10
+ version: string
11
+ }
12
+ paths?: Record<string, unknown>
13
+ servers?: Array<{
14
+ url: string
15
+ description?: string
16
+ }>
17
+ }
18
+
19
+ /**
20
+ * Applies global prefix to OpenAPI servers if needed.
21
+ *
22
+ * @param document - The OpenAPI document to modify
23
+ * @param globalPrefix - The global route prefix (e.g., '/api/v1')
24
+ * @param options - Plugin options that may contain server configuration
25
+ * @returns The document with servers array updated if applicable
26
+ */
27
+ export function applyGlobalPrefix<T extends OpenAPIDocument>(
28
+ document: T,
29
+ globalPrefix: string,
30
+ options: OpenApiGeneratorOptions,
31
+ ): T {
32
+ // If servers are already defined, don't modify
33
+ if (options.servers && options.servers.length > 0) {
34
+ return document
35
+ }
36
+
37
+ // If no global prefix, return as-is
38
+ if (!globalPrefix) {
39
+ return document
40
+ }
41
+
42
+ // Add a default server with the global prefix
43
+ return {
44
+ ...document,
45
+ servers: [
46
+ {
47
+ url: globalPrefix,
48
+ description: 'API with global prefix',
49
+ },
50
+ ],
51
+ }
52
+ }
@@ -0,0 +1 @@
1
+ export { applyGlobalPrefix, type OpenAPIDocument } from './apply-global-prefix.util.mjs'
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "module": "Node18",
5
+ "outDir": "dist"
6
+ },
7
+ "references": [
8
+ { "path": "./tsconfig.lib.json" },
9
+ { "path": "../core" },
10
+ { "path": "../di" },
11
+ { "path": "../openapi" },
12
+ { "path": "../adapter-fastify" }
13
+ ]
14
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist"
5
+ },
6
+ "include": ["src/**/*"],
7
+ "exclude": ["src/**/*.spec.mts", "src/**/*.spec-d.mts"]
8
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist"
5
+ },
6
+ "include": ["src/**/*.mts"]
7
+ }
@@ -0,0 +1,35 @@
1
+ import { withFilter } from 'rolldown/filter'
2
+ import { defineConfig } from 'tsdown'
3
+ import swc from 'unplugin-swc'
4
+
5
+ export default defineConfig({
6
+ entry: ['src/index.mts'],
7
+ outDir: 'lib',
8
+ format: ['esm', 'cjs'],
9
+ clean: true,
10
+ tsconfig: 'tsconfig.lib.json',
11
+ treeshake: true,
12
+ sourcemap: true,
13
+ platform: 'node',
14
+ external: ['@navios/core', '@navios/openapi', '@navios/adapter-fastify', 'fastify'],
15
+ dts: true,
16
+ target: 'es2022',
17
+ plugins: [
18
+ withFilter(
19
+ swc.rolldown({
20
+ jsc: {
21
+ target: 'es2022',
22
+ parser: {
23
+ syntax: 'typescript',
24
+ decorators: true,
25
+ },
26
+ transform: {
27
+ decoratorVersion: '2022-03',
28
+ },
29
+ },
30
+ }),
31
+ // Only run this transform if the file contains a decorator.
32
+ { transform: { code: '@' } },
33
+ ),
34
+ ],
35
+ })
@@ -0,0 +1,11 @@
1
+ import { defineProject } from 'vitest/config'
2
+
3
+ export default defineProject({
4
+ test: {
5
+ typecheck: {
6
+ enabled: true,
7
+ tsconfig: './tsconfig.lib.json',
8
+ },
9
+ include: ['src/**/*.spec.mts', 'src/**/*.spec-d.mts'],
10
+ },
11
+ })