@scalar/import 0.0.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 +7 -0
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/resolve.d.ts +6 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +146 -0
- package/package.json +53 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023-present Scalar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Scalar Import
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@scalar/import)
|
|
4
|
+
[](https://www.npmjs.com/package/@scalar/import)
|
|
5
|
+
[](https://www.npmjs.com/package/@scalar/import)
|
|
6
|
+
[](https://discord.gg/scalar)
|
|
7
|
+
|
|
8
|
+
Pass an URL to an OpenAPI document, a Swagger document, a Postman collection, a Scalar API reference, a Scalar Sandbox link … basically anything, and retrieve an OpenAPI document.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @scalar/import
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
Find any OpenAPI/Swagger document URL in any content:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { resolve } from '@scalar/import'
|
|
22
|
+
|
|
23
|
+
// Get the Swagger 2.0 URL from Swagger UI
|
|
24
|
+
const result = await resolve('https://petstore.swagger.io/')
|
|
25
|
+
|
|
26
|
+
// https://petstore.swagger.io/v2/swagger.json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- Resolves URLs to OpenAPI specifications from various sources
|
|
32
|
+
- Supports JSON and YAML formats (`.json`, `.yaml`, `.yml`)
|
|
33
|
+
- Extracts OpenAPI specification URLs from HTML content, including:
|
|
34
|
+
- Scalar API Reference `<script>` tags
|
|
35
|
+
- Redoc HTML and JavaScript implementations
|
|
36
|
+
- Works with different quote styles and data attribute formats
|
|
37
|
+
- Robust error handling for various HTML structures
|
|
38
|
+
- Transforms GitHub URLs to raw file URLs
|
|
39
|
+
- Handles Scalar Sandbox URLs
|
|
40
|
+
|
|
41
|
+
### Examples
|
|
42
|
+
|
|
43
|
+
| Input | Output | Description |
|
|
44
|
+
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
45
|
+
| https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.json | Same as input | Returns JSON URLs as-is |
|
|
46
|
+
| https://cdn.jsdelivr.net/npm/@scalar/galaxy/dist/latest.yaml or \*.yml | Same as input | Returns YAML URLs as-is |
|
|
47
|
+
| https://sandbox.scalar.com/p/dlw8v | https://sandbox.scalar.com/files/dlw8v/openapi.yaml | Resolves sandbox URLs to specific file paths |
|
|
48
|
+
| https://github.com/owner/repo/blob/main/openapi.yaml | https://raw.githubusercontent.com/owner/repo/refs/heads/main/openapi.yaml | Transforms GitHub URLs to raw file URLs |
|
|
49
|
+
| HTML with data-url attribute | URL from data-url attribute | Extracts URL from HTML script tag with data-url attribute |
|
|
50
|
+
| HTML with relative URL `/openapi.yaml` | https://example.com/openapi.yaml | Resolves relative URLs to absolute URLs |
|
|
51
|
+
| HTML with JSON configuration | URL from JSON configuration | Extracts URL from JSON configuration in data-configuration attribute |
|
|
52
|
+
| Redoc HTML | URL from spec-url attribute | Extracts URL from Redoc's spec-url attribute |
|
|
53
|
+
| HTML with embedded OpenAPI | Parsed OpenAPI object | Extracts and parses embedded OpenAPI JSON from HTML |
|
|
54
|
+
|
|
55
|
+
## Community
|
|
56
|
+
|
|
57
|
+
We are API nerds. You too? Let’s chat on Discord: <https://discord.gg/scalar>
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
The source code in this repository is licensed under [MIT](https://github.com/scalar/openapi-parser/blob/main/LICENSE).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { resolve } from './resolve.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find an OpenAPI document URL in the HTML of @scalar/api-reference and other places.
|
|
3
|
+
* This is useful to open the OpenAPI document from basically any source.
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolve(value?: string): Promise<string | Record<string, any> | undefined>;
|
|
6
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC,CA0DnD"}
|
package/dist/resolve.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find an OpenAPI document URL in the HTML of @scalar/api-reference and other places.
|
|
3
|
+
* This is useful to open the OpenAPI document from basically any source.
|
|
4
|
+
*/
|
|
5
|
+
async function resolve(value) {
|
|
6
|
+
// URLs
|
|
7
|
+
if (value?.startsWith('http://') || value?.startsWith('https://')) {
|
|
8
|
+
// Transform GitHub URLs to raw file URLs
|
|
9
|
+
const githubRawUrl = transformGitHubUrl(value);
|
|
10
|
+
if (githubRawUrl) {
|
|
11
|
+
return githubRawUrl;
|
|
12
|
+
}
|
|
13
|
+
// https://*.json
|
|
14
|
+
if (value?.toLowerCase().endsWith('.json')) {
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
// https://*.yaml
|
|
18
|
+
if (value?.toLowerCase().endsWith('.yaml') ||
|
|
19
|
+
value?.toLowerCase().endsWith('.yml')) {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
// https://sandbox.scalar.com
|
|
23
|
+
const sandboxUrl = value.match(/https:\/\/sandbox\.scalar\.com\/(p|e)\/([a-z0-9]+)/);
|
|
24
|
+
if (sandboxUrl?.[2]) {
|
|
25
|
+
return `https://sandbox.scalar.com/files/${sandboxUrl[2]}/openapi.yaml`;
|
|
26
|
+
}
|
|
27
|
+
// Fetch URL
|
|
28
|
+
try {
|
|
29
|
+
const result = await fetch(value);
|
|
30
|
+
if (result.ok) {
|
|
31
|
+
const content = await result.text();
|
|
32
|
+
const urlOrPath = parseHtml(content);
|
|
33
|
+
if (urlOrPath) {
|
|
34
|
+
return makeRelativeUrlsAbsolute(value, urlOrPath);
|
|
35
|
+
}
|
|
36
|
+
// New: Check for embedded OpenAPI document
|
|
37
|
+
const embeddedSpec = parseEmbeddedOpenApi(content);
|
|
38
|
+
if (embeddedSpec) {
|
|
39
|
+
return embeddedSpec;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
console.warn(`[@scalar/import] Failed to fetch ${value}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error(`[@scalar/import] Failed to fetch ${value}`, error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Go through the HTML and try to find the OpenAPI document URL
|
|
54
|
+
*/
|
|
55
|
+
function parseHtml(html) {
|
|
56
|
+
// Check whether it could be HTML
|
|
57
|
+
if (!html?.includes('<')) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
// data-url="*"
|
|
61
|
+
const dataUrlMatch = html.match(/data-url=["']([^"']+)["']/);
|
|
62
|
+
if (dataUrlMatch?.[1]) {
|
|
63
|
+
return dataUrlMatch[1];
|
|
64
|
+
}
|
|
65
|
+
// spec-url="*"
|
|
66
|
+
const specUrlMatch = html.match(/spec-url=["']([^"']+)["']/);
|
|
67
|
+
if (specUrlMatch?.[1]) {
|
|
68
|
+
return specUrlMatch[1];
|
|
69
|
+
}
|
|
70
|
+
// Redoc.init('*')
|
|
71
|
+
const redocInit = html.match(/Redoc.init\(["']([^"']+)["']/);
|
|
72
|
+
if (redocInit?.[1]) {
|
|
73
|
+
return redocInit[1];
|
|
74
|
+
}
|
|
75
|
+
// &quot;url&quot;:&quot;MY_CUSTOM_URL&quot;
|
|
76
|
+
const configurationUrl = html.match(/&quot;url&quot;:&quot;([^;]+)&quot;/);
|
|
77
|
+
if (configurationUrl?.[1]) {
|
|
78
|
+
return configurationUrl[1];
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* URLs can be relative, but we need absolute URLs eventually.
|
|
84
|
+
*/
|
|
85
|
+
function makeRelativeUrlsAbsolute(baseUrl, path) {
|
|
86
|
+
// Check whether the path is already absolute
|
|
87
|
+
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
88
|
+
return path;
|
|
89
|
+
}
|
|
90
|
+
// Combine the URL and the relative path
|
|
91
|
+
try {
|
|
92
|
+
const { href } = new URL(path, baseUrl);
|
|
93
|
+
return href;
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
// Return original path if URL creation fails
|
|
97
|
+
console.error('[makeRelativeUrlsAbsolute] Error combining URLs:', error);
|
|
98
|
+
return path;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Parse embedded OpenAPI document from HTML
|
|
103
|
+
*/
|
|
104
|
+
function parseEmbeddedOpenApi(html) {
|
|
105
|
+
const match = html.match(/<script[^>]*data-configuration=['"]([^'"]+)['"][^>]*>(.*?)<\/script>/);
|
|
106
|
+
if (!match)
|
|
107
|
+
return undefined;
|
|
108
|
+
try {
|
|
109
|
+
const configString = decodeHtmlEntities(match[1]);
|
|
110
|
+
const config = JSON.parse(configString);
|
|
111
|
+
if (config.spec?.content) {
|
|
112
|
+
return config.spec.content;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
console.error('[@scalar/import] Failed to parse embedded OpenAPI document:', error);
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Decode HTML entities in a string
|
|
122
|
+
*/
|
|
123
|
+
function decodeHtmlEntities(text) {
|
|
124
|
+
const entities = {
|
|
125
|
+
'&': '&',
|
|
126
|
+
'<': '<',
|
|
127
|
+
'>': '>',
|
|
128
|
+
'"': '"',
|
|
129
|
+
''': "'",
|
|
130
|
+
};
|
|
131
|
+
return text.replace(new RegExp(Object.keys(entities).join('|'), 'g'), (match) => entities[match]);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Transform GitHub URLs to raw file URLs, preserving the branch information
|
|
135
|
+
*/
|
|
136
|
+
function transformGitHubUrl(url) {
|
|
137
|
+
const githubRegex = /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)\/(.+)$/;
|
|
138
|
+
const match = url.match(githubRegex);
|
|
139
|
+
if (match) {
|
|
140
|
+
const [, owner, repo, branch, path] = match;
|
|
141
|
+
return `https://raw.githubusercontent.com/${owner}/${repo}/refs/heads/${branch}/${path}`;
|
|
142
|
+
}
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export { resolve };
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@scalar/import",
|
|
3
|
+
"description": "Import any file, URL or content to get an OpenAPI document",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": "Scalar (https://github.com/scalar)",
|
|
6
|
+
"homepage": "https://github.com/scalar/scalar",
|
|
7
|
+
"bugs": "https://github.com/scalar/scalar/issues/new/choose",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/scalar/scalar.git",
|
|
11
|
+
"directory": "packages/import"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"openapi",
|
|
15
|
+
"swagger",
|
|
16
|
+
"postman",
|
|
17
|
+
"scalar"
|
|
18
|
+
],
|
|
19
|
+
"version": "0.0.1",
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "dist/index.js",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"CHANGELOG.md"
|
|
35
|
+
],
|
|
36
|
+
"module": "dist/index.js",
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@scalar/openapi-parser": "0.8.7"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"vite": "^5.2.10",
|
|
42
|
+
"@scalar/build-tooling": "0.1.11"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "scalar-build-rollup",
|
|
46
|
+
"dev": "nodemon --exec \"vite-node playground/index.ts\" --ext ts --quiet",
|
|
47
|
+
"lint:check": "eslint .",
|
|
48
|
+
"lint:fix": "eslint . --fix",
|
|
49
|
+
"test": "vitest",
|
|
50
|
+
"types:build": "scalar-types-build",
|
|
51
|
+
"types:check": "scalar-types-check"
|
|
52
|
+
}
|
|
53
|
+
}
|