@scalar/fastify-api-reference 1.25.130 → 1.26.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @scalar/fastify-api-reference
2
2
 
3
+ ## 1.26.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [0d8a24f]
8
+ - Updated dependencies [4d03e0f]
9
+ - @scalar/openapi-parser@0.10.10
10
+ - @scalar/types@0.0.40
11
+ - @scalar/core@0.1.1
12
+
13
+ ## 1.26.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 54febb3: feat!: drop CommonJS build, please use import('@scalar/api-reference') in Node.js v13.3.0+
18
+
19
+ ### Patch Changes
20
+
21
+ - 117ec7b: fix: one final CI/docker fix for fastify
22
+ - fb22645: fix: fastify docker build standalone copy
23
+
3
24
  ## 1.25.130
4
25
 
5
26
  ### Patch Changes
package/README.md CHANGED
@@ -18,7 +18,7 @@ npm install @scalar/fastify-api-reference
18
18
  And then register it with Fastify:
19
19
 
20
20
  ```ts
21
- await fastify.register(require('@scalar/fastify-api-reference'), {
21
+ await fastify.register(import('@scalar/fastify-api-reference'), {
22
22
  routePrefix: '/reference',
23
23
  })
24
24
  ```
@@ -29,7 +29,7 @@ If you have a OpenAPI/Swagger document already, you can pass an URL to the plugi
29
29
 
30
30
  ```ts
31
31
  // Render an API reference for a given OpenAPI/Swagger spec URL
32
- fastify.register(require('@scalar/fastify-api-reference'), {
32
+ fastify.register(import('@scalar/fastify-api-reference'), {
33
33
  routePrefix: '/reference',
34
34
  configuration: {
35
35
  title: 'Our API Reference',
@@ -43,7 +43,7 @@ fastify.register(require('@scalar/fastify-api-reference'), {
43
43
  With [@fastify/swagger], we’re picking it up automatically, so this would be enough:
44
44
 
45
45
  ```ts
46
- await fastify.register(require('@scalar/fastify-api-reference'), {
46
+ await fastify.register(import('@scalar/fastify-api-reference'), {
47
47
  routePrefix: '/reference',
48
48
  })
49
49
  ```
@@ -57,7 +57,7 @@ The fastify plugin takes our universal configuration object, [read more about co
57
57
  By default, we’re using a custom Fastify theme and it’s beautiful. But you can choose [one of our other themes](https://github.com/scalar/scalar/tree/main/packages/themes), too:
58
58
 
59
59
  ```ts
60
- await fastify.register(require('@scalar/fastify-api-reference'), {
60
+ await fastify.register(import('@scalar/fastify-api-reference'), {
61
61
  routePrefix: '/reference',
62
62
  configuration: {
63
63
  theme: 'purple',
@@ -70,7 +70,7 @@ await fastify.register(require('@scalar/fastify-api-reference'), {
70
70
  The plugin is compatible with the Fastify logger. You can configure the log level for the routes registered by the plugin:
71
71
 
72
72
  ```ts
73
- fastify.register(require('@scalar/fastify-api-reference'), {
73
+ fastify.register(import('@scalar/fastify-api-reference'), {
74
74
  routePrefix: '/reference',
75
75
  logLevel: 'silent',
76
76
  })
@@ -0,0 +1,280 @@
1
+ import { openapi } from '@scalar/openapi-parser';
2
+ import { fetchUrls } from '@scalar/openapi-parser/plugins/fetch-urls';
3
+ import fp from 'fastify-plugin';
4
+ import { slug } from 'github-slugger';
5
+ import { getJavaScriptFile } from './utils/getJavaScriptFile.js';
6
+ import { getHtmlDocument } from './packages/core/dist/libs/html-rendering/index.js';
7
+
8
+ /**
9
+ * Path to the bundled Scalar JavaScript file
10
+ */
11
+ const RELATIVE_JAVASCRIPT_PATH = 'js/scalar.js';
12
+ // This Schema is used to hide the route from the documentation.
13
+ // https://github.com/fastify/fastify-swagger#hide-a-route
14
+ const schemaToHideRoute = {
15
+ hide: true,
16
+ };
17
+ const getRoutePrefix = (routePrefix) => {
18
+ const prefix = routePrefix ?? '/reference';
19
+ // Remove trailing slash if present
20
+ return prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
21
+ };
22
+ /**
23
+ * Get the endpoints for the OpenAPI specification.
24
+ */
25
+ const getOpenApiDocumentEndpoints = (openApiDocumentEndpoints) => {
26
+ const { json = '/openapi.json', yaml = '/openapi.yaml' } = openApiDocumentEndpoints ?? {};
27
+ return { json, yaml };
28
+ };
29
+ /**
30
+ * Get the URL for the Scalar JavaScript file.
31
+ */
32
+ const getJavaScriptUrl = (routePrefix) => `${getRoutePrefix(routePrefix)}/${RELATIVE_JAVASCRIPT_PATH}`.replace(/\/\//g, '/');
33
+ /**
34
+ * The custom theme for Fastify
35
+ */
36
+ const customTheme = `
37
+ .light-mode {
38
+ color-scheme: light;
39
+ --scalar-color-1: #1c1e21;
40
+ --scalar-color-2: #757575;
41
+ --scalar-color-3: #8e8e8e;
42
+ --scalar-color-disabled: #b4b1b1;
43
+ --scalar-color-ghost: #a7a7a7;
44
+ --scalar-color-accent: #2f8555;
45
+ --scalar-background-1: #fff;
46
+ --scalar-background-2: #f5f5f5;
47
+ --scalar-background-3: #ededed;
48
+ --scalar-background-4: rgba(0, 0, 0, 0.06);
49
+ --scalar-background-accent: #2f85551f;
50
+
51
+ --scalar-border-color: rgba(0, 0, 0, 0.1);
52
+ --scalar-scrollbar-color: rgba(0, 0, 0, 0.18);
53
+ --scalar-scrollbar-color-active: rgba(0, 0, 0, 0.36);
54
+ --scalar-lifted-brightness: 1;
55
+ --scalar-backdrop-brightness: 1;
56
+
57
+ --scalar-shadow-1: 0 1px 3px 0 rgba(0, 0, 0, 0.11);
58
+ --scalar-shadow-2: rgba(0, 0, 0, 0.08) 0px 13px 20px 0px,
59
+ rgba(0, 0, 0, 0.08) 0px 3px 8px 0px, #eeeeed 0px 0 0 1px;
60
+
61
+ --scalar-button-1: rgb(49 53 56);
62
+ --scalar-button-1-color: #fff;
63
+ --scalar-button-1-hover: rgb(28 31 33);
64
+
65
+ --scalar-color-green: #007300;
66
+ --scalar-color-red: #af272b;
67
+ --scalar-color-yellow: #b38200;
68
+ --scalar-color-blue: #3b8ba5;
69
+ --scalar-color-orange: #fb892c;
70
+ --scalar-color-purple: #5203d1;
71
+ }
72
+
73
+ .dark-mode {
74
+ color-scheme: dark;
75
+ --scalar-color-1: rgba(255, 255, 255, 0.9);
76
+ --scalar-color-2: rgba(255, 255, 255, 0.62);
77
+ --scalar-color-3: rgba(255, 255, 255, 0.44);
78
+ --scalar-color-disabled: rgba(255, 255, 255, 0.34);
79
+ --scalar-color-ghost: rgba(255, 255, 255, 0.26);
80
+ --scalar-color-accent: #27c2a0;
81
+ --scalar-background-1: #1b1b1d;
82
+ --scalar-background-2: #242526;
83
+ --scalar-background-3: #3b3b3b;
84
+ --scalar-background-4: rgba(255, 255, 255, 0.06);
85
+ --scalar-background-accent: #27c2a01f;
86
+
87
+ --scalar-border-color: rgba(255, 255, 255, 0.1);
88
+ --scalar-scrollbar-color: rgba(255, 255, 255, 0.24);
89
+ --scalar-scrollbar-color-active: rgba(255, 255, 255, 0.48);
90
+ --scalar-lifted-brightness: 1.45;
91
+ --scalar-backdrop-brightness: 0.5;
92
+
93
+ --scalar-shadow-1: 0 1px 3px 0 rgb(0, 0, 0, 0.1);
94
+ --scalar-shadow-2: rgba(15, 15, 15, 0.2) 0px 3px 6px,
95
+ rgba(15, 15, 15, 0.4) 0px 9px 24px, 0 0 0 1px rgba(255, 255, 255, 0.1);
96
+
97
+ --scalar-button-1: #f6f6f6;
98
+ --scalar-button-1-color: #000;
99
+ --scalar-button-1-hover: #e7e7e7;
100
+
101
+ --scalar-color-green: #26b226;
102
+ --scalar-color-red: #fb565b;
103
+ --scalar-color-yellow: #ffc426;
104
+ --scalar-color-blue: #6ecfef;
105
+ --scalar-color-orange: #ff8d4d;
106
+ --scalar-color-purple: #b191f9;
107
+ }
108
+ `;
109
+ /**
110
+ * The default configuration for Fastify
111
+ */
112
+ const DEFAULT_CONFIGURATION = {
113
+ _integration: 'fastify',
114
+ };
115
+ const fastifyApiReference = fp(async (fastify, options) => {
116
+ const { configuration: givenConfiguration } = options;
117
+ // Merge the defaults
118
+ let configuration = {
119
+ ...DEFAULT_CONFIGURATION,
120
+ ...givenConfiguration,
121
+ };
122
+ const specSource = (() => {
123
+ const { content, url } = configuration?.spec ?? {};
124
+ if (content)
125
+ return {
126
+ type: 'content',
127
+ get: () => {
128
+ if (typeof content === 'function')
129
+ return content();
130
+ return content;
131
+ },
132
+ };
133
+ if (url)
134
+ return {
135
+ type: 'url',
136
+ get: () => url,
137
+ };
138
+ if (fastify.hasPlugin('@fastify/swagger')) {
139
+ return {
140
+ type: 'swagger',
141
+ // @ts-ignore We know that @fastify/swagger is loaded.
142
+ get: () => fastify.swagger(),
143
+ };
144
+ }
145
+ return void 0;
146
+ })();
147
+ // If no OpenAPI specification is passed and @fastify/swagger isn’t loaded, show a warning.
148
+ if (!specSource) {
149
+ fastify.log.warn('[@scalar/fastify-api-reference] You didn’t provide a spec.content or spec.url, and @fastify/swagger could not be found. Please provide one of these options.');
150
+ return;
151
+ }
152
+ // Read the JavaScript file once.
153
+ const fileContent = getJavaScriptFile();
154
+ const hooks = {};
155
+ if (options.hooks) {
156
+ const additionalHooks = ['onRequest', 'preHandler'];
157
+ for (const hook of additionalHooks) {
158
+ if (options.hooks[hook])
159
+ hooks[hook] = options.hooks[hook];
160
+ }
161
+ }
162
+ const getLoadedSpecIfAvailable = () => {
163
+ return openapi().load(specSource.get(), { plugins: [fetchUrls()] });
164
+ };
165
+ const getSpecFilenameSlug = async (loadedSpec) => {
166
+ const spec = await loadedSpec?.get();
167
+ // Same GitHub Slugger and default file name as in `@scalar/api-reference`, when generating the download
168
+ return slug(spec?.specification?.info?.title ?? 'spec');
169
+ };
170
+ const openApiSpecUrlJson = `${getRoutePrefix(options.routePrefix)}${getOpenApiDocumentEndpoints(options.openApiDocumentEndpoints).json}`;
171
+ fastify.route({
172
+ method: 'GET',
173
+ url: openApiSpecUrlJson,
174
+ // @ts-ignore We don’t know whether @fastify/swagger is loaded.
175
+ schema: schemaToHideRoute,
176
+ ...hooks,
177
+ ...(options.logLevel && { logLevel: options.logLevel }),
178
+ async handler(_, reply) {
179
+ const spec = getLoadedSpecIfAvailable();
180
+ const filename = await getSpecFilenameSlug(spec);
181
+ const json = JSON.parse(await spec.toJson()); // parsing minifies the JSON
182
+ return reply
183
+ .header('Content-Type', 'application/json')
184
+ .header('Content-Disposition', `filename=${filename}.json`)
185
+ .header('Access-Control-Allow-Origin', '*')
186
+ .header('Access-Control-Allow-Methods', '*')
187
+ .send(json);
188
+ },
189
+ });
190
+ const openApiSpecUrlYaml = `${getRoutePrefix(options.routePrefix)}${getOpenApiDocumentEndpoints(options.openApiDocumentEndpoints).yaml}`;
191
+ fastify.route({
192
+ method: 'GET',
193
+ url: openApiSpecUrlYaml,
194
+ // @ts-ignore We don’t know whether @fastify/swagger is loaded.
195
+ schema: schemaToHideRoute,
196
+ ...hooks,
197
+ ...(options.logLevel && { logLevel: options.logLevel }),
198
+ async handler(_, reply) {
199
+ const spec = getLoadedSpecIfAvailable();
200
+ const filename = await getSpecFilenameSlug(spec);
201
+ const yaml = await spec.toYaml();
202
+ return reply
203
+ .header('Content-Type', 'application/yaml')
204
+ .header('Content-Disposition', `filename=${filename}.yaml`)
205
+ .header('Access-Control-Allow-Origin', '*')
206
+ .header('Access-Control-Allow-Methods', '*')
207
+ .send(yaml);
208
+ },
209
+ });
210
+ // Redirect route without a trailing slash to force a trailing slash:
211
+ // We need this so the request to the JS file is relative.
212
+ // With ignoreTrailingSlash, fastify registeres both routes anyway.
213
+ const doesNotIgnoreTrailingSlash = fastify.initialConfig.ignoreTrailingSlash !== true;
214
+ if (doesNotIgnoreTrailingSlash && getRoutePrefix(options.routePrefix)) {
215
+ fastify.route({
216
+ method: 'GET',
217
+ url: getRoutePrefix(options.routePrefix),
218
+ // @ts-ignore We don't know whether @fastify/swagger is loaded.
219
+ schema: schemaToHideRoute,
220
+ ...hooks,
221
+ ...(options.logLevel && { logLevel: options.logLevel }),
222
+ handler(_, reply) {
223
+ return reply.redirect(getRoutePrefix(options.routePrefix) + '/', 302);
224
+ },
225
+ });
226
+ }
227
+ // If no theme is passed, use the default theme.
228
+ fastify.route({
229
+ method: 'GET',
230
+ url: `${getRoutePrefix(options.routePrefix)}/`,
231
+ // We don’t know whether @fastify/swagger is registered, but it doesn’t hurt to add a schema anyway.
232
+ // @ts-ignore We don’t know whether @fastify/swagger is loaded.
233
+ schema: schemaToHideRoute,
234
+ ...hooks,
235
+ ...(options.logLevel && { logLevel: options.logLevel }),
236
+ handler(_, reply) {
237
+ // Redirect if it’s the route without a slash
238
+ const currentUrl = new URL(_.url, `${_.protocol}://${_.hostname}`);
239
+ if (!currentUrl.pathname.endsWith('/')) {
240
+ return reply.redirect(`${currentUrl.pathname}/`, 301);
241
+ }
242
+ /**
243
+ * Regardless of where we source the spec from, provide it as a URL, to have the
244
+ * download button point to the exposed endpoint.
245
+ * If the URL is explicitly passed, defer to that URL instead.
246
+ */
247
+ if (specSource.type !== 'url') {
248
+ configuration = {
249
+ ...configuration,
250
+ spec: {
251
+ // Use a relative URL in case we're proxied
252
+ url: `.${getOpenApiDocumentEndpoints(options.openApiDocumentEndpoints).json}`,
253
+ },
254
+ };
255
+ }
256
+ // Respond with the HTML document
257
+ return reply.header('Content-Type', 'text/html; charset=utf-8').send(getHtmlDocument({
258
+ // We’re using the bundled JS here by default, but the user can pass a CDN URL.
259
+ cdn: RELATIVE_JAVASCRIPT_PATH,
260
+ ...configuration,
261
+ }, customTheme));
262
+ },
263
+ });
264
+ fastify.route({
265
+ method: 'GET',
266
+ url: getJavaScriptUrl(options.routePrefix),
267
+ // We don’t know whether @fastify/swagger is registered, but it doesn’t hurt to add a schema anyway.
268
+ // @ts-ignore We don’t know whether @fastify/swagger is loaded.
269
+ schema: schemaToHideRoute,
270
+ ...hooks,
271
+ ...(options.logLevel && { logLevel: options.logLevel }),
272
+ handler(_, reply) {
273
+ return reply.header('Content-Type', 'application/javascript; charset=utf-8').send(fileContent);
274
+ },
275
+ });
276
+ }, {
277
+ name: '@scalar/fastify-api-reference',
278
+ });
279
+
280
+ export { customTheme, fastifyApiReference as default };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fastifyApiReference.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastifyApiReference.test.d.ts","sourceRoot":"","sources":["../src/fastifyApiReference.test.ts"],"names":[],"mappings":""}