@scalar/openapi-to-markdown 0.5.44 → 1.0.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 (49) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +73 -34
  3. package/dist/create-markdown-from-openapi.d.ts +16 -15
  4. package/dist/create-markdown-from-openapi.d.ts.map +1 -1
  5. package/dist/create-markdown-from-openapi.js +35 -0
  6. package/dist/index.d.ts +4 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -600
  9. package/dist/load-document.d.ts +4 -0
  10. package/dist/load-document.d.ts.map +1 -0
  11. package/dist/load-document.js +151 -0
  12. package/dist/markdown-nodes.d.ts +25 -0
  13. package/dist/markdown-nodes.d.ts.map +1 -0
  14. package/dist/markdown-nodes.js +34 -0
  15. package/dist/parse-description.d.ts +6 -0
  16. package/dist/parse-description.d.ts.map +1 -0
  17. package/dist/parse-description.js +116 -0
  18. package/dist/parse-html-description.d.ts +4 -0
  19. package/dist/parse-html-description.d.ts.map +1 -0
  20. package/dist/parse-html-description.js +11 -0
  21. package/dist/render-document.d.ts +4 -0
  22. package/dist/render-document.d.ts.map +1 -0
  23. package/dist/render-document.js +112 -0
  24. package/dist/render-operation.d.ts +13 -0
  25. package/dist/render-operation.d.ts.map +1 -0
  26. package/dist/render-operation.js +89 -0
  27. package/dist/render-schema.d.ts +31 -0
  28. package/dist/render-schema.d.ts.map +1 -0
  29. package/dist/render-schema.js +98 -0
  30. package/dist/render-security.d.ts +6 -0
  31. package/dist/render-security.d.ts.map +1 -0
  32. package/dist/render-security.js +33 -0
  33. package/dist/select-document.d.ts +29 -0
  34. package/dist/select-document.d.ts.map +1 -0
  35. package/dist/select-document.js +312 -0
  36. package/package.json +16 -28
  37. package/dist/components/MarkdownReference.test.d.ts +0 -2
  38. package/dist/components/MarkdownReference.test.d.ts.map +0 -1
  39. package/dist/components/MarkdownReference.vue.d.ts +0 -9
  40. package/dist/components/MarkdownReference.vue.d.ts.map +0 -1
  41. package/dist/components/Schema.test.d.ts +0 -2
  42. package/dist/components/Schema.test.d.ts.map +0 -1
  43. package/dist/components/Schema.vue.d.ts +0 -9
  44. package/dist/components/Schema.vue.d.ts.map +0 -1
  45. package/dist/components/XmlOrJson.vue.d.ts +0 -8
  46. package/dist/components/XmlOrJson.vue.d.ts.map +0 -1
  47. package/dist/create-markdown-from-openapi.test.d.ts +0 -2
  48. package/dist/create-markdown-from-openapi.test.d.ts.map +0 -1
  49. package/dist/index.js.map +0 -1
@@ -0,0 +1,98 @@
1
+ import { json2xml } from '@scalar/helpers/file/json2xml';
2
+ import { getExampleFromSchema } from '@scalar/workspace-store/request-example';
3
+ import { resolve } from '@scalar/workspace-store/resolve';
4
+ import { inlineCode, item, list, paragraph, strong, text } from './markdown-nodes.js';
5
+ /** Keep merged reference siblings and sorted properties stable throughout an export. */
6
+ export const createSchemaRenderer = () => {
7
+ const views = new WeakMap();
8
+ const view = (input) => {
9
+ const cached = views.get(input);
10
+ if (cached)
11
+ return cached;
12
+ const schema = resolve.schema(input);
13
+ const value = schema;
14
+ const required = new Set(value.required ?? []);
15
+ const properties = Object.entries(value.properties ?? {})
16
+ .filter((entry) => entry[1] && typeof entry[1] === 'object')
17
+ .sort(([a], [b]) => Number(required.has(b)) - Number(required.has(a)) || a.localeCompare(b));
18
+ const result = { ...value, schema, required, properties };
19
+ views.set(input, result);
20
+ return result;
21
+ };
22
+ const details = (value, property = false) => {
23
+ const type = Array.isArray(value.type) ? value.type.join(' | ') : value.type;
24
+ const nodes = type || property ? [inlineCode(type || 'object')] : [];
25
+ if (value.format)
26
+ nodes.push(text(', format: '), inlineCode(value.format));
27
+ if (value.enum)
28
+ nodes.push(text(', possible values: '), inlineCode(value.enum.map((entry) => JSON.stringify(entry)).join(', ')));
29
+ if (value.default !== undefined)
30
+ nodes.push(text(', default: '), inlineCode(JSON.stringify(value.default)));
31
+ if (value.description)
32
+ nodes.push(text(` — ${value.description}`));
33
+ return nodes;
34
+ };
35
+ const render = (input, depth = 0, ancestors = []) => {
36
+ // The previous renderer expanded a resolved root before tracking child reference strings.
37
+ const identity = depth > 0 && '$ref' in input ? input.$ref : input;
38
+ if (depth >= 10 || ancestors.includes(identity)) {
39
+ return [paragraph({ type: 'emphasis', children: [text('[Circular Reference]')] })];
40
+ }
41
+ const value = view(input);
42
+ const childAncestors = [...ancestors, identity];
43
+ for (const [key, label] of [
44
+ ['allOf', 'All of:'],
45
+ ['anyOf', 'Any of:'],
46
+ ['oneOf', 'One of:'],
47
+ ]) {
48
+ if (value[key])
49
+ return [
50
+ paragraph(strong(text(label))),
51
+ ...value[key].flatMap((child) => render(child, depth + 1, childAncestors)),
52
+ ];
53
+ }
54
+ if (value.not)
55
+ return [paragraph(strong(text('Not:'))), ...render(value.not, depth + 1, childAncestors)];
56
+ if (value.type === 'object' || value.properties.length) {
57
+ const properties = value.properties.map(([name, schema]) => {
58
+ const child = view(schema);
59
+ const label = [inlineCode(name)];
60
+ if (value.required.has(name))
61
+ label.push(text(' (required)'));
62
+ const blocks = [paragraph(strong(...label)), paragraph(...details(child, true))];
63
+ if (child.type === 'object' || child.properties.length) {
64
+ blocks.push(...render(schema, depth + 1, childAncestors));
65
+ }
66
+ if (child.type === 'array' && child.items) {
67
+ blocks.push(paragraph(strong(text('Items:'))), ...render(child.items, depth + 1, childAncestors));
68
+ }
69
+ return item(...blocks);
70
+ });
71
+ return properties.length ? [list(properties)] : [];
72
+ }
73
+ if (value.type === 'array' && value.items) {
74
+ const nodes = [paragraph(strong(text('Array of:'))), ...render(value.items, depth + 1, childAncestors)];
75
+ const constraints = [];
76
+ if (value.minItems !== undefined)
77
+ constraints.push(item(paragraph(text('Min items: '), inlineCode(value.minItems))));
78
+ if (value.maxItems !== undefined)
79
+ constraints.push(item(paragraph(text('Max items: '), inlineCode(value.maxItems))));
80
+ if (value.uniqueItems)
81
+ constraints.push(item(paragraph(text('Unique items: '), inlineCode(true))));
82
+ if (constraints.length)
83
+ nodes.push(list(constraints));
84
+ return nodes;
85
+ }
86
+ const nodes = details(value);
87
+ return nodes.length ? [paragraph(...nodes)] : [];
88
+ };
89
+ const example = (input, xml = false) => {
90
+ const value = getExampleFromSchema(view(input).schema, { xml });
91
+ return {
92
+ type: 'code',
93
+ lang: xml ? 'xml' : 'json',
94
+ value: xml ? json2xml(value) : (JSON.stringify(value, null, 2) ?? ''),
95
+ };
96
+ };
97
+ return { view, render, example };
98
+ };
@@ -0,0 +1,6 @@
1
+ import type { OpenApiDocument } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
2
+ import type { RootContent } from 'mdast';
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[]>;
6
+ //# sourceMappingURL=render-security.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,33 @@
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) => {
5
+ if (!requirements)
6
+ return [];
7
+ const nodes = [heading(4, text('Authentication'))];
8
+ if (!requirements.length)
9
+ nodes.push(paragraph(text('No authentication required.')));
10
+ for (const [index, requirement] of requirements.entries()) {
11
+ if (index)
12
+ nodes.push(paragraph(text('Or:')));
13
+ const entries = Object.entries(requirement);
14
+ if (!entries.length) {
15
+ nodes.push(paragraph(text('No authentication required.')));
16
+ continue;
17
+ }
18
+ const entriesNodes = [];
19
+ for (const [name, scopes] of entries) {
20
+ const blocks = [
21
+ paragraph(strong(text(name)), ...(scopes?.length ? [text(` Scopes: ${scopes.join(', ')}`)] : [])),
22
+ ];
23
+ 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));
29
+ }
30
+ nodes.push(list(entriesNodes));
31
+ }
32
+ return nodes;
33
+ };
@@ -0,0 +1,29 @@
1
+ import type { HttpMethod } from '@scalar/helpers/http/http-methods';
2
+ import type { OpenApiDocument } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
3
+ /** Identify one operation by path and method, operation ID, or JSON pointer. */
4
+ export type OperationSelector = {
5
+ path: string;
6
+ method: HttpMethod | Uppercase<HttpMethod>;
7
+ } | {
8
+ operationId: string;
9
+ } | {
10
+ pointer: string;
11
+ };
12
+ /** Select one reference page, or omit selectors for the whole document. */
13
+ export type OpenApiRenderOptions = {
14
+ [Key in keyof PageSelectors]: Partial<Record<Exclude<keyof PageSelectors, Key>, never>> & Pick<PageSelectors, Key>;
15
+ }[keyof PageSelectors] | Partial<Record<keyof PageSelectors, never>>;
16
+ type PageSelectors = {
17
+ operation: OperationSelector;
18
+ tag: string;
19
+ model: string;
20
+ webhook: {
21
+ name: string;
22
+ method: HttpMethod | Uppercase<HttpMethod>;
23
+ };
24
+ introduction: true;
25
+ };
26
+ /** Scope after resolving references and migrating older documents. */
27
+ export declare const selectDocument: (document: OpenApiDocument, options?: OpenApiRenderOptions) => OpenApiDocument;
28
+ export {};
29
+ //# sourceMappingURL=select-document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-document.d.ts","sourceRoot":"","sources":["../src/select-document.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAA;AAInE,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,8DAA8D,CAAA;AAEnH,gFAAgF;AAChF,MAAM,MAAM,iBAAiB,GACzB;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,CAAA;CAC3C,GACD;IACE,WAAW,EAAE,MAAM,CAAA;CACpB,GACD;IACE,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AACL,2EAA2E;AAC3E,MAAM,MAAM,oBAAoB,GAC5B;KACG,GAAG,IAAI,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,aAAa,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC;CACnH,CAAC,MAAM,aAAa,CAAC,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,aAAa,EAAE,KAAK,CAAC,CAAC,CAAA;AAE/C,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,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,CAAA;KAAE,CAAA;IACrE,YAAY,EAAE,IAAI,CAAA;CACnB,CAAA;AAmKD,sEAAsE;AACtE,eAAO,MAAM,cAAc,GAAI,UAAU,eAAe,EAAE,UAAS,oBAAyB,KAAG,eA+M9F,CAAA"}
@@ -0,0 +1,312 @@
1
+ import { isObject } from '@scalar/helpers/object/is-object';
2
+ import { getPathItemOperation, getResolvedPathItem } from '@scalar/workspace-store/helpers/for-each-path-item-operation';
3
+ import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
4
+ const HTTP_METHODS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'];
5
+ const HTTP_METHOD_SET = new Set(HTTP_METHODS);
6
+ const normalizeHttpMethod = (method) => {
7
+ const normalized = method.toLowerCase();
8
+ if (HTTP_METHOD_SET.has(normalized)) {
9
+ return normalized;
10
+ }
11
+ return null;
12
+ };
13
+ const normalizeJsonPointer = (pointer) => {
14
+ if (/~(?![01])/.test(pointer)) {
15
+ throw new Error(`Invalid JSON pointer escape in "${pointer}"`);
16
+ }
17
+ if (pointer.startsWith('#/')) {
18
+ return pointer.slice(1);
19
+ }
20
+ if (pointer.startsWith('/')) {
21
+ return pointer;
22
+ }
23
+ throw new Error(`Invalid JSON pointer "${pointer}". JSON pointers must start with "#/"`);
24
+ };
25
+ const parseJsonPointer = (pointer) => normalizeJsonPointer(pointer)
26
+ .slice(1)
27
+ .split('/')
28
+ .map((segment) => segment.replaceAll('~1', '/').replaceAll('~0', '~'));
29
+ const getOperationSelectorFromPointer = (pointer) => {
30
+ const segments = parseJsonPointer(pointer);
31
+ if (segments.length !== 3 || segments[0] !== 'paths') {
32
+ throw new Error(`JSON pointer "${pointer}" must target an operation object under "/paths/{path}/{method}"`);
33
+ }
34
+ const path = segments[1];
35
+ const method = segments[2];
36
+ if (!path || !method) {
37
+ throw new Error(`JSON pointer "${pointer}" must target an operation object under "/paths/{path}/{method}"`);
38
+ }
39
+ return {
40
+ path,
41
+ method: method,
42
+ };
43
+ };
44
+ const getPathEntries = (document) => {
45
+ const paths = document.paths;
46
+ if (!isObject(paths)) {
47
+ return [];
48
+ }
49
+ return Object.entries(paths).flatMap(([path, pathItemRef]) => {
50
+ const pathItem = getResolvedPathItem(pathItemRef);
51
+ return pathItem ? [[path, pathItem]] : [];
52
+ });
53
+ };
54
+ const filterPathItemToSingleOperation = (pathItem, selectedMethod) => Object.fromEntries(Object.entries(pathItem).filter(([key]) => {
55
+ const method = normalizeHttpMethod(key);
56
+ return !method || method === selectedMethod;
57
+ }));
58
+ const findOperationByPathAndMethod = (document, selector) => {
59
+ const method = normalizeHttpMethod(selector.method);
60
+ if (!method) {
61
+ throw new Error(`Invalid HTTP method "${selector.method}". Supported methods: ${HTTP_METHODS.join(', ')}`);
62
+ }
63
+ const pathItemRef = document.paths?.[selector.path];
64
+ if (!getPathItemOperation(pathItemRef, method)) {
65
+ throw new Error(`Operation not found for path "${selector.path}" and method "${method.toUpperCase()}"`);
66
+ }
67
+ return {
68
+ path: selector.path,
69
+ method,
70
+ };
71
+ };
72
+ const findOperationsByOperationId = (document, operationId) => getPathEntries(document).flatMap(([path, pathItem]) => Object.entries(pathItem).flatMap(([methodKey, operation]) => {
73
+ const method = normalizeHttpMethod(methodKey);
74
+ if (!method || !isObject(operation)) {
75
+ return [];
76
+ }
77
+ const candidateOperationId = 'operationId' in operation && typeof operation.operationId === 'string' ? operation.operationId : undefined;
78
+ if (candidateOperationId !== operationId) {
79
+ return [];
80
+ }
81
+ return [{ path, method }];
82
+ }));
83
+ const resolveOperationMatch = (document, selector) => {
84
+ if ('pointer' in selector) {
85
+ return findOperationByPathAndMethod(document, getOperationSelectorFromPointer(selector.pointer));
86
+ }
87
+ if ('operationId' in selector) {
88
+ const matches = findOperationsByOperationId(document, selector.operationId);
89
+ if (!matches.length) {
90
+ throw new Error(`Operation with operationId "${selector.operationId}" was not found`);
91
+ }
92
+ if (matches.length > 1) {
93
+ const uniqueCandidates = matches.map(({ path, method }) => `"${method.toUpperCase()} ${path}"`);
94
+ throw new Error(`Multiple operations found for operationId "${selector.operationId}". Use { path, method } instead. Matches: ${uniqueCandidates.join(', ')}`);
95
+ }
96
+ return matches[0];
97
+ }
98
+ return findOperationByPathAndMethod(document, selector);
99
+ };
100
+ const filterDocumentByOperation = (document, selector) => {
101
+ const match = resolveOperationMatch(document, selector);
102
+ const pathItem = getPathEntries(document).find(([path]) => path === match.path)?.[1];
103
+ if (!pathItem) {
104
+ throw new Error(`Operation not found for path "${match.path}" and method "${match.method.toUpperCase()}"`);
105
+ }
106
+ return {
107
+ ...document,
108
+ paths: {
109
+ [match.path]: filterPathItemToSingleOperation(pathItem, match.method),
110
+ },
111
+ };
112
+ };
113
+ /** Scope after resolving references and migrating older documents. */
114
+ export const selectDocument = (document, options = {}) => {
115
+ if (!isObject(options)) {
116
+ throw new Error('Render options must be an object');
117
+ }
118
+ const keys = Object.keys(options).filter((key) => options[key] !== undefined);
119
+ if (!keys.length) {
120
+ return document;
121
+ }
122
+ if (keys.length !== 1 || !['operation', 'tag', 'model', 'webhook', 'introduction'].includes(keys[0])) {
123
+ throw new Error('Specify exactly one of operation, tag, model, webhook, or introduction');
124
+ }
125
+ if (options.introduction !== undefined && options.introduction !== true) {
126
+ throw new Error('Introduction selector must be true');
127
+ }
128
+ for (const key of ['tag', 'model']) {
129
+ if (options[key] !== undefined && (typeof options[key] !== 'string' || !options[key].length)) {
130
+ throw new Error(`${key} selector must be a non-empty string`);
131
+ }
132
+ }
133
+ if (options.operation !== undefined) {
134
+ const selector = options.operation;
135
+ if (!isObject(selector) ||
136
+ !((Object.keys(selector).length === 1 &&
137
+ ('operationId' in selector
138
+ ? typeof selector.operationId === 'string' && selector.operationId.length
139
+ : 'pointer' in selector && typeof selector.pointer === 'string' && selector.pointer.length)) ||
140
+ (Object.keys(selector).length === 2 &&
141
+ 'path' in selector &&
142
+ typeof selector.path === 'string' &&
143
+ 'method' in selector &&
144
+ typeof selector.method === 'string'))) {
145
+ throw new Error('Invalid operation selector. Use { path, method }, { operationId }, or { pointer }');
146
+ }
147
+ }
148
+ const selected = { ...document, paths: {}, webhooks: {}, tags: [] };
149
+ const modelRoots = [];
150
+ if (options.operation) {
151
+ selected.paths = filterDocumentByOperation(document, options.operation).paths;
152
+ }
153
+ if (options.tag !== undefined) {
154
+ const metadata = document.tags?.filter((tag) => tag.name === options.tag) ?? [];
155
+ if (metadata.length > 1) {
156
+ throw new Error(`Multiple tags found for "${options.tag}"`);
157
+ }
158
+ selected.tags = metadata.length ? metadata : [{ name: options.tag }];
159
+ for (const [path, item] of getPathEntries(document)) {
160
+ const methods = HTTP_METHODS.filter((method) => getPathItemOperation(item, method)?.tags?.includes(options.tag));
161
+ if (methods.length) {
162
+ selected.paths[path] = Object.fromEntries(Object.entries(item).filter(([key]) => !HTTP_METHOD_SET.has(key) || methods.includes(key)));
163
+ }
164
+ }
165
+ if (!metadata.length && !Object.keys(selected.paths ?? {}).length) {
166
+ throw new Error(`Tag "${options.tag}" was not found`);
167
+ }
168
+ }
169
+ if (options.model !== undefined) {
170
+ const schema = document.components?.schemas?.[options.model];
171
+ if (schema === undefined || !Object.hasOwn(document.components?.schemas ?? {}, options.model)) {
172
+ throw new Error(`Model "${options.model}" was not found`);
173
+ }
174
+ modelRoots.push(schema);
175
+ }
176
+ if (options.webhook !== undefined) {
177
+ const selector = options.webhook;
178
+ if (!isObject(selector) ||
179
+ typeof selector.name !== 'string' ||
180
+ !selector.name ||
181
+ typeof selector.method !== 'string' ||
182
+ Object.keys(selector).length !== 2) {
183
+ throw new Error('Invalid webhook selector. Use { name, method }');
184
+ }
185
+ const method = normalizeHttpMethod(selector.method);
186
+ if (!method) {
187
+ throw new Error(`Invalid HTTP method "${selector.method}"`);
188
+ }
189
+ const item = getResolvedPathItem(document.webhooks?.[selector.name]);
190
+ if (!item || !getPathItemOperation(item, method)) {
191
+ throw new Error(`Webhook "${selector.name}" with method "${method.toUpperCase()}" was not found`);
192
+ }
193
+ selected.webhooks = { [selector.name]: filterPathItemToSingleOperation(item, method) };
194
+ }
195
+ const securityNames = new Set();
196
+ const tagNames = new Set();
197
+ for (const items of [selected.paths, selected.webhooks]) {
198
+ for (const [path, itemRef] of Object.entries(items ?? {})) {
199
+ const item = getResolvedPathItem(itemRef);
200
+ const scoped = { ...item, parameters: undefined, servers: undefined };
201
+ for (const method of HTTP_METHODS) {
202
+ const operation = getPathItemOperation(item, method);
203
+ if (!operation) {
204
+ continue;
205
+ }
206
+ const parameters = new Map();
207
+ for (const ref of [...(item.parameters ?? []), ...(operation.parameters ?? [])]) {
208
+ const parameter = getResolvedRef(ref);
209
+ if (parameter) {
210
+ parameters.set(`${parameter.in}:${parameter.name}`, ref);
211
+ }
212
+ }
213
+ const security = operation.security ?? document.security ?? [];
214
+ for (const requirement of security) {
215
+ for (const name of Object.keys(requirement)) {
216
+ securityNames.add(name);
217
+ }
218
+ }
219
+ for (const name of operation.tags ?? []) {
220
+ tagNames.add(name);
221
+ }
222
+ scoped[method] = {
223
+ ...operation,
224
+ parameters: [...parameters.values()],
225
+ servers: operation.servers ?? item.servers ?? document.servers,
226
+ security,
227
+ tags: options.tag !== undefined ? [options.tag] : operation.tags,
228
+ };
229
+ }
230
+ items[path] = scoped;
231
+ }
232
+ }
233
+ if (options.operation || options.webhook) {
234
+ selected.tags = document.tags?.filter((tag) => tagNames.has(tag.name)) ?? [];
235
+ }
236
+ if (options.introduction) {
237
+ for (const requirement of document.security ?? []) {
238
+ for (const name of Object.keys(requirement)) {
239
+ securityNames.add(name);
240
+ }
241
+ }
242
+ }
243
+ else {
244
+ selected.servers = [];
245
+ selected.security = undefined;
246
+ }
247
+ const schemas = document.components?.schemas ?? {};
248
+ const needed = new Set(options.model !== undefined ? [options.model] : []);
249
+ const visited = new WeakSet();
250
+ const references = new Set();
251
+ const opaqueValues = new Set(['example', 'examples', 'default', 'enum', 'const', 'value', 'dataValue']);
252
+ const namedMaps = new Set([
253
+ 'paths',
254
+ 'webhooks',
255
+ 'responses',
256
+ 'content',
257
+ 'headers',
258
+ 'links',
259
+ 'encoding',
260
+ 'variables',
261
+ 'parameters',
262
+ 'requestBodies',
263
+ 'securitySchemes',
264
+ 'pathItems',
265
+ 'callbacks',
266
+ 'mediaTypes',
267
+ 'additionalOperations',
268
+ 'schemas',
269
+ 'properties',
270
+ 'patternProperties',
271
+ '$defs',
272
+ 'definitions',
273
+ 'dependentSchemas',
274
+ ]);
275
+ const visit = (value, namedLevels = 0) => {
276
+ if (!value || typeof value !== 'object' || visited.has(value)) {
277
+ return;
278
+ }
279
+ visited.add(value);
280
+ if (!namedLevels && '$ref' in value && typeof value.$ref === 'string') {
281
+ const ref = value.$ref;
282
+ if (!references.has(ref)) {
283
+ references.add(ref);
284
+ if (ref.startsWith('#/components/schemas/')) {
285
+ const name = parseJsonPointer(ref)[2];
286
+ if (Object.hasOwn(schemas, name)) {
287
+ needed.add(name);
288
+ visit(schemas[name]);
289
+ }
290
+ }
291
+ visit(getResolvedRef(value), namedLevels);
292
+ }
293
+ }
294
+ for (const [key, child] of Object.entries(value)) {
295
+ // Names such as "example" are valid map entries; only keyword positions hold opaque data.
296
+ if (key !== '$ref-value' && (namedLevels || (!opaqueValues.has(key) && !key.startsWith('x-')))) {
297
+ const childNamedLevels = namedLevels ? namedLevels - 1 : key === 'callbacks' ? 2 : namedMaps.has(key) ? 1 : 0;
298
+ visit(child, childNamedLevels);
299
+ }
300
+ }
301
+ };
302
+ visit({ paths: selected.paths, webhooks: selected.webhooks });
303
+ for (const root of modelRoots) {
304
+ visit(root);
305
+ }
306
+ selected.components = {
307
+ ...document.components,
308
+ schemas: Object.fromEntries(Object.entries(schemas).filter(([name]) => needed.has(name))),
309
+ securitySchemes: Object.fromEntries(Object.entries(document.components?.securitySchemes ?? {}).filter(([name]) => securityNames.has(name))),
310
+ };
311
+ return selected;
312
+ };
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "llm",
17
17
  "swagger"
18
18
  ],
19
- "version": "0.5.44",
19
+ "version": "1.0.0",
20
20
  "engines": {
21
21
  "node": ">=22"
22
22
  },
@@ -28,16 +28,6 @@
28
28
  "import": "./dist/index.js",
29
29
  "types": "./dist/index.d.ts",
30
30
  "default": "./dist/index.js"
31
- },
32
- "./*.css": {
33
- "import": "./dist/*.css",
34
- "require": "./dist/*.css",
35
- "default": "./dist/*.css"
36
- },
37
- "./css/*.css": {
38
- "import": "./dist/css/*.css",
39
- "require": "./dist/css/*.css",
40
- "default": "./dist/css/*.css"
41
31
  }
42
32
  },
43
33
  "files": [
@@ -45,34 +35,32 @@
45
35
  "CHANGELOG.md"
46
36
  ],
47
37
  "dependencies": {
48
- "html-minifier-terser": "^7.2.0",
38
+ "@scalar/code-highlight": "0.4.6",
39
+ "@scalar/helpers": "0.12.0",
40
+ "@scalar/json-magic": "0.14.0",
41
+ "@scalar/openapi-upgrader": "0.2.17",
42
+ "@scalar/workspace-store": "0.62.0",
49
43
  "rehype-parse": "^9.0.1",
50
44
  "rehype-remark": "^10.0.1",
51
45
  "rehype-sanitize": "^6.0.0",
46
+ "rehype-stringify": "^10.0.1",
52
47
  "remark-gfm": "^4.0.1",
48
+ "remark-parse": "^11.0.0",
49
+ "remark-rehype": "^11.1.2",
53
50
  "remark-stringify": "^11.0.0",
54
- "unified": "^11.0.5",
55
- "vue": "^3.5.40",
56
- "@scalar/components": "0.29.1",
57
- "@scalar/json-magic": "0.13.4",
58
- "@scalar/helpers": "0.11.3",
59
- "@scalar/workspace-store": "0.60.0"
51
+ "unified": "^11.0.5"
60
52
  },
61
53
  "devDependencies": {
62
54
  "@hono/node-server": "^1.19.10",
63
- "@types/html-minifier-terser": "^7.0.2",
64
- "@vitejs/plugin-vue": "^6.0.8",
65
- "@vue/test-utils": "2.4.11",
66
- "hono": "^4.12.7",
67
- "vite": "8.1.5",
68
- "vitest": "4.1.10",
69
- "vue": "^3.5.40",
70
- "@scalar/galaxy": "0.7.0"
55
+ "@scalar/galaxy": "0.7.1",
56
+ "@types/mdast": "^4.0.4",
57
+ "hono": "^4.13.7",
58
+ "vitest": "4.1.10"
71
59
  },
72
60
  "scripts": {
73
- "build": "vite build && vue-tsc -p tsconfig.build.json",
61
+ "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
74
62
  "dev": "tsx watch playground/index.ts",
75
63
  "test": "vitest --run",
76
- "types:check": "vue-tsc --noEmit"
64
+ "types:check": "tsgo --noEmit"
77
65
  }
78
66
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=MarkdownReference.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MarkdownReference.test.d.ts","sourceRoot":"","sources":["../../src/components/MarkdownReference.test.ts"],"names":[],"mappings":""}
@@ -1,9 +0,0 @@
1
- import type { OpenApiDocument } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document';
2
- type MarkdownDocument = Partial<OpenApiDocument> & Pick<OpenApiDocument, 'openapi' | 'info'>;
3
- type __VLS_Props = {
4
- content: MarkdownDocument;
5
- };
6
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
- declare const _default: typeof __VLS_export;
8
- export default _default;
9
- //# sourceMappingURL=MarkdownReference.vue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MarkdownReference.vue.d.ts","sourceRoot":"","sources":["../../src/components/MarkdownReference.vue"],"names":[],"mappings":"AA4kBA,OAAO,KAAK,EACV,eAAe,EAMhB,MAAM,8DAA8D,CAAA;AAQrE,KAAK,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,GAC9C,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,MAAM,CAAC,CAAA;AAuC3C,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE,gBAAgB,CAAA;CAC1B,CAAC;AAouBF,QAAA,MAAM,YAAY,kSAEhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=Schema.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Schema.test.d.ts","sourceRoot":"","sources":["../../src/components/Schema.test.ts"],"names":[],"mappings":""}
@@ -1,9 +0,0 @@
1
- import type { MaybeRefSchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/schema';
2
- type __VLS_Props = {
3
- schema: MaybeRefSchemaObject;
4
- depth?: number;
5
- };
6
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
- declare const _default: typeof __VLS_export;
8
- export default _default;
9
- //# sourceMappingURL=Schema.vue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Schema.vue.d.ts","sourceRoot":"","sources":["../../src/components/Schema.vue"],"names":[],"mappings":"AAiZA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oDAAoD,CAAA;AAI9F,KAAK,WAAW,GAAG;IACjB,MAAM,EAAE,oBAAoB,CAAA;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAC;AAqhBF,QAAA,MAAM,YAAY,kSAEhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -1,8 +0,0 @@
1
- type __VLS_Props = {
2
- xml?: boolean;
3
- modelValue: unknown;
4
- };
5
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
- declare const _default: typeof __VLS_export;
7
- export default _default;
8
- //# sourceMappingURL=XmlOrJson.vue.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"XmlOrJson.vue.d.ts","sourceRoot":"","sources":["../../src/components/XmlOrJson.vue"],"names":[],"mappings":"AA2BA,KAAK,WAAW,GAAG;IACjB,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,UAAU,EAAE,OAAO,CAAA;CACpB,CAAC;AA+CF,QAAA,MAAM,YAAY,kSAEhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=create-markdown-from-openapi.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create-markdown-from-openapi.test.d.ts","sourceRoot":"","sources":["../src/create-markdown-from-openapi.test.ts"],"names":[],"mappings":""}