@dogsbay/format-openapi 0.2.0-beta.13 → 0.2.0-beta.130
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/cli.d.ts +0 -1
- package/dist/cli.js +3 -1
- package/dist/code-samples.d.ts +0 -1
- package/dist/code-samples.js +10 -1
- package/dist/core.d.ts +111 -0
- package/dist/core.js +1224 -0
- package/dist/embed.d.ts +29 -0
- package/dist/embed.js +134 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -2
- package/dist/parse.d.ts +10 -7
- package/dist/parse.js +87 -424
- package/dist/slug.d.ts +0 -1
- package/dist/slug.js +0 -1
- package/dist/spec-path.d.ts +33 -0
- package/dist/spec-path.js +68 -0
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -1
- package/package.json +23 -8
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/code-samples.d.ts.map +0 -1
- package/dist/code-samples.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/parse.d.ts.map +0 -1
- package/dist/parse.js.map +0 -1
- package/dist/slug.d.ts.map +0 -1
- package/dist/slug.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
package/dist/embed.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The spec references an embed names, in order. Each is the string the
|
|
3
|
+
* viewer would request, and whether it is resolved against the page URL
|
|
4
|
+
* (`window.location.pathname + "../api.yaml"`, or any relative URL) or the
|
|
5
|
+
* site root (`/api.yaml`). URLs (`https:`) are not returned.
|
|
6
|
+
*/
|
|
7
|
+
export declare function embeddedSpecRefs(html: string): string[];
|
|
8
|
+
export interface EmbedSpecOptions {
|
|
9
|
+
/** The source root; the spec must be inside it once symlinks resolve. */
|
|
10
|
+
srcDir: string;
|
|
11
|
+
/** The page's docname: `reference/api/ams-api` (no suffix). */
|
|
12
|
+
docname: string;
|
|
13
|
+
/** Directories copied to the site root (Sphinx `html_extra_path`). */
|
|
14
|
+
extraDirs?: readonly string[];
|
|
15
|
+
/** Directories served at `/_static/` (Sphinx `html_static_path`). */
|
|
16
|
+
staticDirs?: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The source file a spec reference loads, as a `/`-separated path relative
|
|
20
|
+
* to `srcDir`, or null.
|
|
21
|
+
*
|
|
22
|
+
* A relative reference is resolved against the page URL. Sphinx serves a
|
|
23
|
+
* page at `/reference/api/ams-api/` (dirhtml) or `/reference/api/ams-api.html`
|
|
24
|
+
* (html), and the two resolve `../` differently, so both are tried. The site
|
|
25
|
+
* path is then looked up where Sphinx would have put that file: the source
|
|
26
|
+
* tree, `html_extra_path` (copied to the root) and `html_static_path`
|
|
27
|
+
* (served at `/_static/`). Last, a file of the same name next to the page.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveEmbeddedSpec(ref: string, opts: EmbedSpecOptions): string | null;
|
package/dist/embed.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A spec embedded in a page by a JavaScript API viewer: Swagger UI, Redoc or
|
|
3
|
+
* RapiDoc. Importers drop raw `<script>`, which leaves such a page EMPTY:
|
|
4
|
+
* the reference was never in the page, the viewer drew it in the browser
|
|
5
|
+
* from a spec file. This finds that file so the importer can emit
|
|
6
|
+
* `:::openapi{spec="…"}` instead, and the site build renders the spec.
|
|
7
|
+
*
|
|
8
|
+
* No dependencies (it is imported by format-rst and format-myst, which do not
|
|
9
|
+
* need the spec parser), and nothing is fetched: only a file in the source
|
|
10
|
+
* tree is ever returned.
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync, realpathSync, statSync } from "node:fs";
|
|
13
|
+
import { join, posix, relative, sep } from "node:path";
|
|
14
|
+
const SPEC_FILE = /\.(ya?ml|json)$/i;
|
|
15
|
+
/** An embed is a few hundred bytes; a larger block is not scanned. Every
|
|
16
|
+
* pattern below is also linear: `[^<>]` cannot run on into the next tag. */
|
|
17
|
+
const MAX_HTML = 64 * 1024;
|
|
18
|
+
/**
|
|
19
|
+
* The spec references an embed names, in order. Each is the string the
|
|
20
|
+
* viewer would request, and whether it is resolved against the page URL
|
|
21
|
+
* (`window.location.pathname + "../api.yaml"`, or any relative URL) or the
|
|
22
|
+
* site root (`/api.yaml`). URLs (`https:`) are not returned.
|
|
23
|
+
*/
|
|
24
|
+
export function embeddedSpecRefs(html) {
|
|
25
|
+
const refs = [];
|
|
26
|
+
if (html.length > MAX_HTML)
|
|
27
|
+
return refs;
|
|
28
|
+
const add = (ref) => {
|
|
29
|
+
if (!ref)
|
|
30
|
+
return;
|
|
31
|
+
const clean = ref.trim();
|
|
32
|
+
if (!clean || /^[a-z][a-z0-9+.-]*:|^\/\//i.test(clean))
|
|
33
|
+
return;
|
|
34
|
+
if (!SPEC_FILE.test(clean.split(/[?#]/)[0]))
|
|
35
|
+
return;
|
|
36
|
+
if (!refs.includes(clean))
|
|
37
|
+
refs.push(clean);
|
|
38
|
+
};
|
|
39
|
+
// Swagger UI: SwaggerUIBundle({ url: … }) / SwaggerUI({ url: … }), and
|
|
40
|
+
// `urls: [{ url: … }]`. The value may concatenate location.pathname with a
|
|
41
|
+
// relative literal; the literal is what matters.
|
|
42
|
+
if (/\bSwaggerUI(?:Bundle)?\s*\(/.test(html)) {
|
|
43
|
+
for (const m of html.matchAll(/\burl\s*:\s*([^,\n}]+)/g)) {
|
|
44
|
+
const literals = [...m[1].matchAll(/(["'`])((?:(?!\1)[^\\\n]|\\.)*)\1/g)].map((l) => l[2]);
|
|
45
|
+
add(literals.join(""));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Redoc: Redoc.init("spec.yaml", …) and <redoc spec-url="…">.
|
|
49
|
+
for (const m of html.matchAll(/\bRedoc\.init\s*\(\s*(["'`])([^"'`\n]+)\1/g))
|
|
50
|
+
add(m[2]);
|
|
51
|
+
// Redoc and RapiDoc elements.
|
|
52
|
+
for (const m of html.matchAll(/<(?:redoc|rapi-doc)\b[^<>]*?\bspec-url\s*=\s*(["'])([^"'<>]+)\1/gi))
|
|
53
|
+
add(m[2]);
|
|
54
|
+
return refs;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The source file a spec reference loads, as a `/`-separated path relative
|
|
58
|
+
* to `srcDir`, or null.
|
|
59
|
+
*
|
|
60
|
+
* A relative reference is resolved against the page URL. Sphinx serves a
|
|
61
|
+
* page at `/reference/api/ams-api/` (dirhtml) or `/reference/api/ams-api.html`
|
|
62
|
+
* (html), and the two resolve `../` differently, so both are tried. The site
|
|
63
|
+
* path is then looked up where Sphinx would have put that file: the source
|
|
64
|
+
* tree, `html_extra_path` (copied to the root) and `html_static_path`
|
|
65
|
+
* (served at `/_static/`). Last, a file of the same name next to the page.
|
|
66
|
+
*/
|
|
67
|
+
export function resolveEmbeddedSpec(ref, opts) {
|
|
68
|
+
const path = ref.split(/[?#]/)[0].replace(/\\/g, "/");
|
|
69
|
+
const docname = opts.docname.replace(/\\/g, "/");
|
|
70
|
+
const docDir = posix.dirname(docname) === "." ? "" : posix.dirname(docname);
|
|
71
|
+
const pageDirs = docname === "index" || docname.endsWith("/index") ? [docDir] : [docname, docDir];
|
|
72
|
+
const sitePaths = [];
|
|
73
|
+
if (path.startsWith("/")) {
|
|
74
|
+
sitePaths.push(path.replace(/^\/+/, ""));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
for (const base of pageDirs) {
|
|
78
|
+
const joined = posix.normalize(posix.join("/", base, path));
|
|
79
|
+
// posix.join("/", …) clamps at the root; a reference that climbs above
|
|
80
|
+
// it under one URL shape is simply not that shape.
|
|
81
|
+
if (climbsAbove(base, path))
|
|
82
|
+
continue;
|
|
83
|
+
sitePaths.push(joined.replace(/^\/+/, ""));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const candidates = [];
|
|
87
|
+
for (const p of sitePaths) {
|
|
88
|
+
candidates.push(join(opts.srcDir, p));
|
|
89
|
+
for (const d of opts.extraDirs ?? [])
|
|
90
|
+
candidates.push(join(d, p));
|
|
91
|
+
if (p.startsWith("_static/")) {
|
|
92
|
+
for (const d of opts.staticDirs ?? [])
|
|
93
|
+
candidates.push(join(d, p.slice("_static/".length)));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
candidates.push(join(opts.srcDir, docDir, posix.basename(path)));
|
|
97
|
+
let realRoot;
|
|
98
|
+
try {
|
|
99
|
+
realRoot = realpathSync(opts.srcDir);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
for (const c of candidates) {
|
|
105
|
+
if (!existsSync(c))
|
|
106
|
+
continue;
|
|
107
|
+
let real;
|
|
108
|
+
try {
|
|
109
|
+
real = realpathSync(c);
|
|
110
|
+
if (!statSync(real).isFile())
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (!real.startsWith(realRoot + sep))
|
|
117
|
+
continue;
|
|
118
|
+
return relative(realRoot, real).split(sep).join("/");
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
/** Does `rel`, resolved from the directory `base`, go above the root? */
|
|
123
|
+
function climbsAbove(base, rel) {
|
|
124
|
+
let depth = base ? base.split("/").filter(Boolean).length : 0;
|
|
125
|
+
for (const seg of rel.split("/")) {
|
|
126
|
+
if (seg === "..") {
|
|
127
|
+
if (--depth < 0)
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
else if (seg && seg !== ".")
|
|
131
|
+
depth++;
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { SchemaProperty, Parameter, ResponseDef, CodeSample, EndpointProps, } from "./types.js";
|
|
2
|
-
export { parseOpenApiSpec, detectOpenApiSource, detectSpecPath } from "./parse.js";
|
|
3
|
-
export type { ParseOpenApiOptions } from "./parse.js";
|
|
2
|
+
export { parseOpenApiSpec, detectOpenApiSource, detectSpecPath, openApiEmbed, loadSpec } from "./parse.js";
|
|
3
|
+
export type { ParseOpenApiOptions, OpenApiEmbed, OpenApiEmbedOptions } from "./parse.js";
|
|
4
|
+
export { createSpecCache, directiveOperation, embedSpec, findSpec, parseSpecText, prepareSpec, specCandidates, specToPages, VALUE_TOO_LARGE, type SpecLookup, } from "./core.js";
|
|
4
5
|
export { operationToSlug, slugifyTag } from "./slug.js";
|
|
5
6
|
export { resolveCodeSamples } from "./code-samples.js";
|
|
6
7
|
export { plugin } from "./cli.js";
|
|
7
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { parseOpenApiSpec, detectOpenApiSource, detectSpecPath } from "./parse.js";
|
|
1
|
+
export { parseOpenApiSpec, detectOpenApiSource, detectSpecPath, openApiEmbed, loadSpec } from "./parse.js";
|
|
2
|
+
// The Worker-safe core is also its own entry, `@dogsbay/format-openapi/core`.
|
|
3
|
+
export { createSpecCache, directiveOperation, embedSpec, findSpec, parseSpecText, prepareSpec, specCandidates, specToPages, VALUE_TOO_LARGE, } from "./core.js";
|
|
2
4
|
export { operationToSlug, slugifyTag } from "./slug.js";
|
|
3
5
|
export { resolveCodeSamples } from "./code-samples.js";
|
|
4
6
|
export { plugin } from "./cli.js";
|
|
5
|
-
//# sourceMappingURL=index.js.map
|
package/dist/parse.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ExportPage, NavItem } from "@dogsbay/types";
|
|
2
|
+
import { type OpenApiEmbed, type OpenApiEmbedOptions, type ParseOpenApiOptions } from "./core.js";
|
|
3
|
+
export type { OpenApiEmbed, OpenApiEmbedOptions, ParseOpenApiOptions };
|
|
2
4
|
/** Auto-detect spec location given the configured source path. */
|
|
3
5
|
export declare function detectSpecPath(sourcePath: string): string | null;
|
|
4
6
|
/**
|
|
@@ -7,14 +9,15 @@ export declare function detectSpecPath(sourcePath: string): string | null;
|
|
|
7
9
|
* extension whose contents declare `openapi:`).
|
|
8
10
|
*/
|
|
9
11
|
export declare function detectOpenApiSource(path: string): boolean;
|
|
10
|
-
export interface ParseOpenApiOptions {
|
|
11
|
-
/** When the importer wants nav hrefs to start with this prefix. */
|
|
12
|
-
hrefPrefix?: string;
|
|
13
|
-
/** Sub-path within the namespace for emitted pages, e.g. "api". */
|
|
14
|
-
section?: string;
|
|
15
|
-
}
|
|
16
12
|
export declare function parseOpenApiSpec(sourcePath: string, options?: ParseOpenApiOptions): Promise<{
|
|
17
13
|
pages: ExportPage[];
|
|
18
14
|
nav: NavItem[];
|
|
19
15
|
}>;
|
|
20
|
-
|
|
16
|
+
/** Read a spec file (or URL) and prepare it (core.ts `prepareSpec`). */
|
|
17
|
+
export declare function loadSpec(specPath: string): Promise<any>;
|
|
18
|
+
/**
|
|
19
|
+
* A whole spec as the BODY of one page (core.ts `embedSpec`), read from a
|
|
20
|
+
* file. Each call parses the spec; a build that embeds one spec on several
|
|
21
|
+
* pages uses `createSpecCache` and `embedSpec` instead.
|
|
22
|
+
*/
|
|
23
|
+
export declare function openApiEmbed(specPath: string, options?: OpenApiEmbedOptions): Promise<OpenApiEmbed>;
|