@scalar/openapi-to-markdown 1.4.0 → 1.5.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 (39) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +66 -5
  3. package/dist/document-anchors.d.ts +8 -0
  4. package/dist/document-anchors.d.ts.map +1 -0
  5. package/dist/document-anchors.js +25 -0
  6. package/dist/document-examples.d.ts +14 -0
  7. package/dist/document-examples.d.ts.map +1 -0
  8. package/dist/document-examples.js +54 -0
  9. package/dist/get-markdown-examples.d.ts +2 -0
  10. package/dist/get-markdown-examples.d.ts.map +1 -1
  11. package/dist/get-markdown-examples.js +11 -12
  12. package/dist/markdown-nodes.d.ts +22 -1
  13. package/dist/markdown-nodes.d.ts.map +1 -1
  14. package/dist/markdown-nodes.js +4 -0
  15. package/dist/parse-description.d.ts +2 -0
  16. package/dist/parse-description.d.ts.map +1 -1
  17. package/dist/parse-description.js +28 -1
  18. package/dist/render-document.d.ts +2 -2
  19. package/dist/render-document.d.ts.map +1 -1
  20. package/dist/render-document.js +217 -84
  21. package/dist/render-examples.d.ts +9 -1
  22. package/dist/render-examples.d.ts.map +1 -1
  23. package/dist/render-examples.js +26 -8
  24. package/dist/render-operation-details.d.ts +3 -2
  25. package/dist/render-operation-details.d.ts.map +1 -1
  26. package/dist/render-operation-details.js +8 -5
  27. package/dist/render-operation.d.ts +16 -1
  28. package/dist/render-operation.d.ts.map +1 -1
  29. package/dist/render-operation.js +168 -42
  30. package/dist/render-schema.d.ts +30 -3
  31. package/dist/render-schema.d.ts.map +1 -1
  32. package/dist/render-schema.js +474 -111
  33. package/dist/render-security.d.ts +7 -3
  34. package/dist/render-security.d.ts.map +1 -1
  35. package/dist/render-security.js +61 -12
  36. package/dist/select-document.d.ts +5 -0
  37. package/dist/select-document.d.ts.map +1 -1
  38. package/dist/select-document.js +3 -2
  39. package/package.json +16 -7
@@ -1,6 +1,10 @@
1
1
  import type { OpenApiDocument } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
2
- import type { RootContent } from 'mdast';
2
+ import type { Heading, RootContent } from 'mdast';
3
3
  import type { DescriptionParser } from './parse-description.js';
4
- /** Preserve OR between requirements and AND between schemes within a requirement. */
5
- export declare const renderSecurity: (requirements: OpenApiDocument["security"], schemes: NonNullable<OpenApiDocument["components"]>["securitySchemes"], description: DescriptionParser) => Promise<RootContent[]>;
4
+ /**
5
+ * Preserve OR between requirements and AND between schemes within a requirement.
6
+ * Without declared requirements there is nothing to say: only an explicit empty list means that
7
+ * no authentication is required.
8
+ */
9
+ export declare const renderSecurity: (requirements: OpenApiDocument["security"], schemes: NonNullable<OpenApiDocument["components"]>["securitySchemes"], description: DescriptionParser, level?: Heading["depth"]) => Promise<RootContent[]>;
6
10
  //# sourceMappingURL=render-security.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-security.d.ts","sourceRoot":"","sources":["../src/render-security.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8DAA8D,CAAA;AACnG,OAAO,KAAK,EAAY,WAAW,EAAE,MAAM,OAAO,CAAA;AAGlD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAE5D,qFAAqF;AACrF,eAAO,MAAM,cAAc,GACzB,cAAc,eAAe,CAAC,UAAU,CAAC,EACzC,SAAS,WAAW,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,iBAAiB,CAAC,EACtE,aAAa,iBAAiB,KAC7B,OAAO,CAAC,WAAW,EAAE,CA0BvB,CAAA"}
1
+ {"version":3,"file":"render-security.d.ts","sourceRoot":"","sources":["../src/render-security.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8DAA8D,CAAA;AACnG,OAAO,KAAK,EAAE,OAAO,EAA6B,WAAW,EAAE,MAAM,OAAO,CAAA;AAG5E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAsD5D;;;;GAIG;AACH,eAAO,MAAM,cAAc,GACzB,cAAc,eAAe,CAAC,UAAU,CAAC,EACzC,SAAS,WAAW,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAC,iBAAiB,CAAC,EACtE,aAAa,iBAAiB,EAC9B,QAAO,OAAO,CAAC,OAAO,CAAK,KAC1B,OAAO,CAAC,WAAW,EAAE,CAsBvB,CAAA"}
@@ -1,10 +1,61 @@
1
1
  import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
2
- import { heading, item, list, paragraph, strong, text } from './markdown-nodes.js';
3
- /** Preserve OR between requirements and AND between schemes within a requirement. */
4
- export const renderSecurity = async (requirements, schemes, description) => {
2
+ import { heading, inlineCode, item, list, paragraph, strong, text } from './markdown-nodes.js';
3
+ const flowNames = {
4
+ implicit: 'implicit',
5
+ password: 'password',
6
+ clientCredentials: 'client credentials',
7
+ authorizationCode: 'authorization code',
8
+ deviceAuthorization: 'device authorization',
9
+ };
10
+ /** Summarize a security scheme on one line, for example "API key in header `X-Api-Key`". */
11
+ const summarizeScheme = (scheme) => {
12
+ const value = scheme;
13
+ switch (value.type) {
14
+ case 'apiKey':
15
+ return [text(`API key in ${String(value.in ?? 'header')} `), inlineCode(value.name)];
16
+ case 'http': {
17
+ const name = typeof value.scheme === 'string' ? value.scheme.toLowerCase() : '';
18
+ return [
19
+ text(`HTTP ${name || 'authentication'}`),
20
+ ...(typeof value.bearerFormat === 'string' ? [text(' ('), inlineCode(value.bearerFormat), text(')')] : []),
21
+ ];
22
+ }
23
+ case 'oauth2': {
24
+ const flows = Object.entries(typeof value.flows === 'object' && value.flows ? value.flows : {});
25
+ const nodes = [text('OAuth 2.0')];
26
+ for (const [index, [flow, settings]] of flows.entries()) {
27
+ const urls = settings;
28
+ nodes.push(text(`${index ? '; ' : ': '}${flowNames[flow] ?? flow}`));
29
+ for (const [key, label] of [
30
+ ['authorizationUrl', 'authorize'],
31
+ ['deviceAuthorizationUrl', 'device authorization'],
32
+ ['tokenUrl', 'token'],
33
+ ['refreshUrl', 'refresh'],
34
+ ]) {
35
+ // Coercion fills in optional URLs as empty strings.
36
+ if (typeof urls[key] === 'string' && urls[key])
37
+ nodes.push(text(`, ${label} `), inlineCode(urls[key]));
38
+ }
39
+ }
40
+ return nodes;
41
+ }
42
+ case 'openIdConnect':
43
+ return [text('OpenID Connect '), inlineCode(value.openIdConnectUrl)];
44
+ case 'mutualTLS':
45
+ return [text('Mutual TLS')];
46
+ default:
47
+ return [inlineCode(value.type)];
48
+ }
49
+ };
50
+ /**
51
+ * Preserve OR between requirements and AND between schemes within a requirement.
52
+ * Without declared requirements there is nothing to say: only an explicit empty list means that
53
+ * no authentication is required.
54
+ */
55
+ export const renderSecurity = async (requirements, schemes, description, level = 4) => {
5
56
  if (!requirements)
6
57
  return [];
7
- const nodes = [heading(4, text('Authentication'))];
58
+ const nodes = [heading(level, text('Authentication'))];
8
59
  if (!requirements.length)
9
60
  nodes.push(paragraph(text('No authentication required.')));
10
61
  for (const [index, requirement] of requirements.entries()) {
@@ -17,15 +68,13 @@ export const renderSecurity = async (requirements, schemes, description) => {
17
68
  }
18
69
  const entriesNodes = [];
19
70
  for (const [name, scopes] of entries) {
20
- const blocks = [
21
- paragraph(strong(text(name)), ...(scopes?.length ? [text(` Scopes: ${scopes.join(', ')}`)] : [])),
22
- ];
23
71
  const scheme = getResolvedRef(schemes?.[name]);
24
- if (scheme) {
25
- blocks.push({ type: 'code', value: JSON.stringify(scheme, null, 2) });
26
- blocks.push(...(await description(scheme.description)));
27
- }
28
- entriesNodes.push(item(...blocks));
72
+ const line = [strong(text(name))];
73
+ if (scheme)
74
+ line.push(text(': '), ...summarizeScheme(scheme));
75
+ if (scopes?.length)
76
+ line.push(text(', scopes: '), inlineCode(scopes.join(', ')));
77
+ entriesNodes.push(item(paragraph(...line), ...(await description(scheme?.description))));
29
78
  }
30
79
  nodes.push(list(entriesNodes));
31
80
  }
@@ -23,6 +23,11 @@ export type SchemaReferenceOptions = {
23
23
  ref: string;
24
24
  name: string;
25
25
  }) => string | undefined;
26
+ /**
27
+ * Render references to primitive, enum and `const` schemas, and to aliases of them, in place
28
+ * instead of linking them. Their whole definition fits on one line. Defaults to `true`.
29
+ */
30
+ inlinePrimitives?: boolean;
26
31
  };
27
32
  };
28
33
  type PageSelectors = {
@@ -1 +1 @@
1
- {"version":3,"file":"select-document.d.ts","sourceRoot":"","sources":["../src/select-document.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAA;AACjF,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,8DAA8D,CAAA;AAEnH,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GACzB;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,eAAe,CAAA;CACxB,GACD;IACE,WAAW,EAAE,MAAM,CAAA;CACpB,GACD;IACE,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AACL,2EAA2E;AAC3E,MAAM,MAAM,oBAAoB,GAAG,sBAAsB,GACvD,CACI;KACG,GAAG,IAAI,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,aAAa,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,GACrF,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC;CAC3B,CAAC,MAAM,aAAa,CAAC,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,aAAa,EAAE,KAAK,CAAC,CAAC,CAC9C,CAAA;AAEH,mFAAmF;AACnF,MAAM,MAAM,sBAAsB,GAAG;IACnC,4GAA4G;IAC5G,gBAAgB,CAAC,EAAE;QACjB,IAAI,EAAE,QAAQ,CAAA;QACd,oFAAoF;QACpF,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,KAAK,MAAM,GAAG,SAAS,CAAA;KAC9E,CAAA;CACF,CAAA;AAED,KAAK,aAAa,GAAG;IACnB,SAAS,EAAE,iBAAiB,CAAA;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,eAAe,CAAA;KAAE,CAAA;IAClD,YAAY,EAAE,IAAI,CAAA;CACnB,CAAA;AAyKD,sEAAsE;AACtE,eAAO,MAAM,cAAc,GAAI,UAAU,eAAe,EAAE,UAAS,oBAAyB,KAAG,eA2N9F,CAAA"}
1
+ {"version":3,"file":"select-document.d.ts","sourceRoot":"","sources":["../src/select-document.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAA;AACjF,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,8DAA8D,CAAA;AAEnH,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GACzB;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,eAAe,CAAA;CACxB,GACD;IACE,WAAW,EAAE,MAAM,CAAA;CACpB,GACD;IACE,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AACL,2EAA2E;AAC3E,MAAM,MAAM,oBAAoB,GAAG,sBAAsB,GACvD,CACI;KACG,GAAG,IAAI,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,aAAa,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,GACrF,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC;CAC3B,CAAC,MAAM,aAAa,CAAC,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,aAAa,EAAE,KAAK,CAAC,CAAC,CAC9C,CAAA;AAEH,mFAAmF;AACnF,MAAM,MAAM,sBAAsB,GAAG;IACnC,4GAA4G;IAC5G,gBAAgB,CAAC,EAAE;QACjB,IAAI,EAAE,QAAQ,CAAA;QACd,oFAAoF;QACpF,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,KAAK,MAAM,GAAG,SAAS,CAAA;QAC7E;;;WAGG;QACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAC3B,CAAA;CACF,CAAA;AAED,KAAK,aAAa,GAAG;IACnB,SAAS,EAAE,iBAAiB,CAAA;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,eAAe,CAAA;KAAE,CAAA;IAClD,YAAY,EAAE,IAAI,CAAA;CACnB,CAAA;AAyKD,sEAAsE;AACtE,eAAO,MAAM,cAAc,GAAI,UAAU,eAAe,EAAE,UAAS,oBAAyB,KAAG,eA4N9F,CAAA"}
@@ -231,8 +231,9 @@ export const selectDocument = (document, options = {}) => {
231
231
  parameters.set(`${parameter.in}:${parameter.name}`, ref);
232
232
  }
233
233
  }
234
- const security = operation.security ?? document.security ?? [];
235
- for (const requirement of security) {
234
+ // Undeclared security stays undeclared: an empty list would claim that no authentication is required.
235
+ const security = operation.security ?? document.security;
236
+ for (const requirement of security ?? []) {
236
237
  for (const name of Object.keys(requirement)) {
237
238
  securityNames.add(name);
238
239
  }
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "llm",
17
17
  "swagger"
18
18
  ],
19
- "version": "1.4.0",
19
+ "version": "1.5.0",
20
20
  "engines": {
21
21
  "node": ">=22"
22
22
  },
@@ -44,7 +44,7 @@
44
44
  "@scalar/helpers": "0.16.0",
45
45
  "@scalar/json-magic": "0.15.3",
46
46
  "@scalar/openapi-upgrader": "0.4.1",
47
- "@scalar/workspace-store": "0.67.1",
47
+ "@scalar/workspace-store": "0.67.2",
48
48
  "rehype-parse": "^9.0.1",
49
49
  "rehype-remark": "^10.0.1",
50
50
  "rehype-sanitize": "^6.0.0",
@@ -54,16 +54,25 @@
54
54
  "unified": "^11.0.5"
55
55
  },
56
56
  "devDependencies": {
57
- "@hono/node-server": "^2.1.1",
57
+ "@scalar/components": "0.30.5",
58
58
  "@scalar/galaxy": "0.7.1",
59
+ "@scalar/themes": "0.18.1",
60
+ "@tailwindcss/vite": "^4.3.3",
59
61
  "@types/mdast": "^4.0.4",
60
- "hono": "^4.13.8",
61
- "vitest": "4.1.10"
62
+ "@vitejs/plugin-vue": "^6.0.8",
63
+ "marked": "^14.0.0",
64
+ "rehype-stringify": "^10.0.1",
65
+ "tailwindcss": "^4.3.3",
66
+ "tsx": "4.19.4",
67
+ "vite": "8.1.5",
68
+ "vitest": "4.1.10",
69
+ "vue": "^3.5.40",
70
+ "vue-tsc": "^3.3.8"
62
71
  },
63
72
  "scripts": {
64
73
  "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
65
- "dev": "tsx watch playground/index.ts",
74
+ "dev": "cd playground && vite",
66
75
  "test": "vitest --run",
67
- "types:check": "tsgo --noEmit"
76
+ "types:check": "tsgo --noEmit && vue-tsc --noEmit -p playground/tsconfig.json"
68
77
  }
69
78
  }