@scalar/import 0.0.3 → 0.0.5

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,21 @@
1
1
  # @scalar/import
2
2
 
3
+ ## 0.0.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 779cf35: feat: import from escaped JS object
8
+ - Updated dependencies [757fade]
9
+ - Updated dependencies [a607115]
10
+ - @scalar/openapi-parser@0.8.9
11
+
12
+ ## 0.0.4
13
+
14
+ ### Patch Changes
15
+
16
+ - 2275977: feat: import from way more sources, leverage the proxy more
17
+ - f2361b3: fix: resolve JSON/YAML URLs
18
+
3
19
  ## 0.0.3
4
20
 
5
21
  ### Patch Changes
package/dist/resolve.d.ts CHANGED
@@ -2,5 +2,10 @@
2
2
  * Find an OpenAPI document URL in the HTML of @scalar/api-reference and other places.
3
3
  * This is useful to open the OpenAPI document from basically any source.
4
4
  */
5
- export declare function resolve(value?: string | null): Promise<string | Record<string, any> | undefined>;
5
+ export declare function resolve(value?: string | null, options?: {
6
+ /**
7
+ * Fetch function to use instead of the global fetch. Use this to intercept requests.
8
+ */
9
+ fetch?: (url: string) => Promise<Response>;
10
+ }): Promise<string | Record<string, any> | undefined>;
6
11
  //# sourceMappingURL=resolve.d.ts.map
@@ -1 +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,GAAG,IAAI,GACpB,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC,CA0DnD"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,EACrB,OAAO,CAAC,EAAE;IACR;;OAEG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CAC3C,GACA,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC,CAmHnD"}
package/dist/resolve.js CHANGED
@@ -1,8 +1,10 @@
1
+ import { parse } from 'yaml';
2
+
1
3
  /**
2
4
  * Find an OpenAPI document URL in the HTML of @scalar/api-reference and other places.
3
5
  * This is useful to open the OpenAPI document from basically any source.
4
6
  */
5
- async function resolve(value) {
7
+ async function resolve(value, options) {
6
8
  // URLs
7
9
  if (value?.startsWith('http://') || value?.startsWith('https://')) {
8
10
  // Transform GitHub URLs to raw file URLs
@@ -26,14 +28,69 @@ async function resolve(value) {
26
28
  }
27
29
  // Fetch URL
28
30
  try {
29
- const result = await fetch(value);
31
+ let result = await (options?.fetch
32
+ ? options.fetch(value)
33
+ : fetch(value, {
34
+ cache: 'no-cache',
35
+ }));
36
+ // If the custom fetch failed, try again with regular fetch
37
+ if (!result.ok && options?.fetch) {
38
+ result = await fetch(value, {
39
+ cache: 'no-cache',
40
+ });
41
+ }
30
42
  if (result.ok) {
31
43
  const content = await result.text();
32
- const urlOrPath = parseHtml(content);
33
- if (urlOrPath) {
34
- return makeRelativeUrlsAbsolute(value, urlOrPath);
44
+ // Check if content is directly JSON/YAML
45
+ try {
46
+ // Try parsing as JSON
47
+ const jsonContent = JSON.parse(content);
48
+ if (jsonContent.openapi || jsonContent.swagger) {
49
+ return value;
50
+ }
51
+ }
52
+ catch {
53
+ // Try parsing as YAML
54
+ try {
55
+ const yamlContent = parse(content);
56
+ if (yamlContent?.openapi || yamlContent?.swagger) {
57
+ return value;
58
+ }
59
+ }
60
+ catch {
61
+ // Not YAML
62
+ }
63
+ }
64
+ const urlOrPathOrDocument = parseHtml(content);
65
+ // Document (string)
66
+ if (typeof urlOrPathOrDocument === 'string') {
67
+ try {
68
+ // JSON?
69
+ return JSON.parse(urlOrPathOrDocument);
70
+ }
71
+ catch {
72
+ // No JSON
73
+ try {
74
+ // YAML?
75
+ const yaml = parse(urlOrPathOrDocument);
76
+ if (typeof yaml === 'object') {
77
+ return yaml;
78
+ }
79
+ }
80
+ catch {
81
+ // Not YAML
82
+ }
83
+ }
35
84
  }
36
- // New: Check for embedded OpenAPI document
85
+ // Document (object)
86
+ if (typeof urlOrPathOrDocument === 'object') {
87
+ return urlOrPathOrDocument;
88
+ }
89
+ // Relative or absolute URL
90
+ if (urlOrPathOrDocument) {
91
+ return makeRelativeUrlsAbsolute(value, urlOrPathOrDocument);
92
+ }
93
+ // Check for embedded OpenAPI document
37
94
  const embeddedSpec = parseEmbeddedOpenApi(content);
38
95
  if (embeddedSpec) {
39
96
  return embeddedSpec;
@@ -57,10 +114,13 @@ function parseHtml(html) {
57
114
  if (!html?.includes('<')) {
58
115
  return undefined;
59
116
  }
60
- // data-url="*"
61
- const dataUrlMatch = html.match(/data-url=["']([^"']+)["']/);
62
- if (dataUrlMatch?.[1]) {
63
- return dataUrlMatch[1];
117
+ // id="api-reference" data-url="*"
118
+ const dataUrlMatch = html
119
+ .match(/id=["']api-reference["'][\s\S]*?data-url=["']([^"']+)["']/)
120
+ ?.slice(1)
121
+ .find(Boolean);
122
+ if (dataUrlMatch) {
123
+ return dataUrlMatch;
64
124
  }
65
125
  // spec-url="*"
66
126
  const specUrlMatch = html.match(/spec-url=["']([^"']+)["']/);
@@ -73,9 +133,57 @@ function parseHtml(html) {
73
133
  return redocInit[1];
74
134
  }
75
135
  // &amp;quot;url&amp;quot;:&amp;quot;MY_CUSTOM_URL&amp;quot;
76
- const configurationUrl = html.match(/&amp;quot;url&amp;quot;:&amp;quot;([^;]+)&amp;quot;/);
77
- if (configurationUrl?.[1]) {
78
- return configurationUrl[1];
136
+ const doubleEncodedConfigurationUrl = html.match(/&amp;quot;url&amp;quot;:&amp;quot;([^;]+)&amp;quot;/);
137
+ if (doubleEncodedConfigurationUrl?.[1]) {
138
+ return doubleEncodedConfigurationUrl[1];
139
+ }
140
+ // &amp;quot;url&amp;quot;:&amp;quot;MY_CUSTOM_URL&amp;quot;
141
+ const encodedConfigurationUrl = html.match(/&quot;url&quot;:&quot;([^;]+)&quot;/);
142
+ if (encodedConfigurationUrl?.[1]) {
143
+ return encodedConfigurationUrl[1];
144
+ }
145
+ // Try to find embedded OpenAPI document in script tag first
146
+ const scriptContent = parseScriptContent(html);
147
+ if (scriptContent) {
148
+ return scriptContent;
149
+ }
150
+ // Check for configuration in script tag
151
+ const scriptConfigMatch = html.match(/url:\s*["']([^"']+)["']/i);
152
+ if (scriptConfigMatch?.[1]) {
153
+ return scriptConfigMatch?.[1];
154
+ }
155
+ // Check for OpenAPI URLs in the HTML
156
+ const linkMatch = html.match(/<a[^>]*href=["']([^"']+\.(?:yaml|yml|json))["'][^>]*>/i);
157
+ if (linkMatch?.[1]) {
158
+ return linkMatch[1];
159
+ }
160
+ // Check for URLs in escaped JS objects
161
+ const escapedJsonMatch = html.match(/\\"spec\\":\{.*?\\"url\\":\\"([^"\\]+)\\"/);
162
+ if (escapedJsonMatch?.[1]) {
163
+ return escapedJsonMatch[1];
164
+ }
165
+ return undefined;
166
+ }
167
+ /**
168
+ * Parse OpenAPI document directly from script tag content
169
+ */
170
+ function parseScriptContent(html) {
171
+ const match = html.match(/<script[^>]*id="api-reference"[^>]*>([\s\S]*?)<\/script>/);
172
+ if (!match?.[1])
173
+ return undefined;
174
+ try {
175
+ const content = match[1].trim();
176
+ if (content) {
177
+ try {
178
+ return JSON.parse(content);
179
+ }
180
+ catch {
181
+ return parse(content);
182
+ }
183
+ }
184
+ }
185
+ catch (error) {
186
+ console.error('[@scalar/import] Failed to parse script content:', error);
79
187
  }
80
188
  return undefined;
81
189
  }
@@ -102,13 +210,19 @@ function makeRelativeUrlsAbsolute(baseUrl, path) {
102
210
  * Parse embedded OpenAPI document from HTML
103
211
  */
104
212
  function parseEmbeddedOpenApi(html) {
105
- const match = html.match(/<script[^>]*data-configuration=['"]([^'"]+)['"][^>]*>(.*?)<\/script>/);
213
+ const match = html.match(/<script[^>]*data-configuration=['"]([^'"]+)['"][^>]*>(.*?)<\/script>/s);
106
214
  if (!match)
107
215
  return undefined;
108
216
  try {
109
217
  const configString = decodeHtmlEntities(match[1]);
110
218
  const config = JSON.parse(configString);
219
+ // Handle both direct JSON content and YAML content
111
220
  if (config.spec?.content) {
221
+ // If content is a string, assume it's YAML
222
+ if (typeof config.spec.content === 'string') {
223
+ return parse(config.spec.content);
224
+ }
225
+ // If content is an object, return it directly
112
226
  return config.spec.content;
113
227
  }
114
228
  }
@@ -128,7 +242,15 @@ function decodeHtmlEntities(text) {
128
242
  '&quot;': '"',
129
243
  '&#39;': "'",
130
244
  };
131
- return text.replace(new RegExp(Object.keys(entities).join('|'), 'g'), (match) => entities[match]);
245
+ const updatedText = text
246
+ .replace(new RegExp(Object.keys(entities).join('|'), 'g'), (match) => entities[match])
247
+ .replace(/\n/g, '\\n')
248
+ .trim();
249
+ if (updatedText.startsWith('{&quot;')) {
250
+ // console.log('ok', updatedText)
251
+ return decodeHtmlEntities(updatedText);
252
+ }
253
+ return updatedText;
132
254
  }
133
255
  /**
134
256
  * Transform GitHub URLs to raw file URLs, preserving the branch information
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "postman",
17
17
  "scalar"
18
18
  ],
19
- "version": "0.0.3",
19
+ "version": "0.0.5",
20
20
  "engines": {
21
21
  "node": ">=18"
22
22
  },
@@ -35,7 +35,8 @@
35
35
  ],
36
36
  "module": "dist/index.js",
37
37
  "dependencies": {
38
- "@scalar/openapi-parser": "0.8.8"
38
+ "yaml": "^2.4.5",
39
+ "@scalar/openapi-parser": "0.8.9"
39
40
  },
40
41
  "devDependencies": {
41
42
  "vite": "^5.4.9",