@mermaid-js/mermaid-cli 11.15.0 → 11.16.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.
- package/README.md +17 -11
- package/dist-types/src/index.d.ts +5 -6
- package/dist-types/src/index.d.ts.map +1 -1
- package/dist-types/src/puppeteerIntercept.d.ts +10 -11
- package/dist-types/src/puppeteerIntercept.d.ts.map +1 -1
- package/dist-types/src/version.d.ts +1 -1
- package/dist-types/src/version.d.ts.map +1 -1
- package/package.json +10 -12
- package/src/cli.js +5 -3
- package/src/index.js +586 -342
- package/src/puppeteerIntercept.js +82 -66
- package/src/version.js +1 -1
- package/src/index.js.bak +0 -540
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* @import puppeteer from 'puppeteer';
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { readFile, realpath } from
|
|
6
|
-
import path from
|
|
7
|
-
import url from
|
|
5
|
+
import { readFile, realpath } from "node:fs/promises";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import url from "node:url";
|
|
8
|
+
import { DEFAULT_INTERCEPT_RESOLUTION_PRIORITY } from "puppeteer";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Guesses the MIME-type of a file based on its extension.
|
|
@@ -13,19 +14,19 @@ import url from 'node:url'
|
|
|
13
14
|
*
|
|
14
15
|
* @param {string} filePath - The file path to guess the MIME-type for.
|
|
15
16
|
*/
|
|
16
|
-
function getContentTypeFromFileExtension
|
|
17
|
-
const ext = path.extname(filePath).toLowerCase()
|
|
17
|
+
function getContentTypeFromFileExtension(filePath) {
|
|
18
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
18
19
|
switch (ext) {
|
|
19
|
-
case
|
|
20
|
+
case ".css":
|
|
20
21
|
// Make sure to set UTF-8, since sometimes Puppeteer can parse it as Latin-1.
|
|
21
|
-
return
|
|
22
|
-
case
|
|
23
|
-
case
|
|
24
|
-
return
|
|
25
|
-
case
|
|
26
|
-
return
|
|
22
|
+
return "text/css;charset=UTF-8";
|
|
23
|
+
case ".js":
|
|
24
|
+
case ".mjs":
|
|
25
|
+
return "application/javascript";
|
|
26
|
+
case ".woff2":
|
|
27
|
+
return "font/woff2";
|
|
27
28
|
default:
|
|
28
|
-
throw new Error(`Unsupported file extension for intercept: ${ext}`)
|
|
29
|
+
throw new Error(`Unsupported file extension for intercept: ${ext}`);
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
|
|
@@ -36,87 +37,102 @@ function getContentTypeFromFileExtension (filePath) {
|
|
|
36
37
|
* instead intercepts dummy `https://mermaid-cli-intercept.invalid` requests.
|
|
37
38
|
*/
|
|
38
39
|
export class Interceptor {
|
|
39
|
-
#INTERCEPT_ORIGIN =
|
|
40
|
+
#INTERCEPT_ORIGIN = "https://mermaid-cli-intercept.invalid";
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
#allowedDirs = new Set()
|
|
43
|
+
* Set of allowed file directories that can be intercepted.
|
|
44
|
+
*
|
|
45
|
+
* This is used to prevent arbitrary file access through the intercept mechanism.
|
|
46
|
+
*
|
|
47
|
+
* Make sure to use `realpath` to resolve any symlinks.
|
|
48
|
+
*
|
|
49
|
+
* @type {Set<string>}
|
|
50
|
+
*/
|
|
51
|
+
#allowedDirs = new Set();
|
|
51
52
|
|
|
52
53
|
/**
|
|
53
54
|
* @param {URL | `file://${string}`} fileUrl - File URL
|
|
54
55
|
* @param {Object} [options] - Optional options.
|
|
55
56
|
* @param {number} [options.allowParentDirectoryLevel] - Number of parent directory levels to allow access to.
|
|
56
57
|
*/
|
|
57
|
-
async fileUrlToInterceptUrl
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (fileUrl.protocol !== 'file:') {
|
|
62
|
-
throw new Error(`Invalid file URL: ${fileUrl}`)
|
|
58
|
+
async fileUrlToInterceptUrl(fileUrl, { allowParentDirectoryLevel = 1 } = {}) {
|
|
59
|
+
fileUrl = new URL(fileUrl);
|
|
60
|
+
if (fileUrl.protocol !== "file:") {
|
|
61
|
+
throw new Error(`Invalid file URL: ${fileUrl}`);
|
|
63
62
|
}
|
|
64
|
-
let parentDirectory = await realpath(url.fileURLToPath(fileUrl))
|
|
63
|
+
let parentDirectory = await realpath(url.fileURLToPath(fileUrl));
|
|
65
64
|
while (allowParentDirectoryLevel-- >= 0) {
|
|
66
|
-
parentDirectory = path.dirname(parentDirectory)
|
|
65
|
+
parentDirectory = path.dirname(parentDirectory);
|
|
67
66
|
}
|
|
68
|
-
this.#allowedDirs.add(parentDirectory)
|
|
69
|
-
return `${this.#INTERCEPT_ORIGIN}${fileUrl.pathname}
|
|
67
|
+
this.#allowedDirs.add(parentDirectory);
|
|
68
|
+
return `${this.#INTERCEPT_ORIGIN}${fileUrl.pathname}`;
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
/**
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
async interceptUrlToFileUrl
|
|
78
|
-
interceptUrl = new URL(interceptUrl)
|
|
72
|
+
*
|
|
73
|
+
* @param {URL | string} interceptUrl
|
|
74
|
+
* @throws {Error} If the URL is not a valid intercept URL
|
|
75
|
+
*/
|
|
76
|
+
async interceptUrlToFileUrl(interceptUrl) {
|
|
77
|
+
interceptUrl = new URL(interceptUrl);
|
|
79
78
|
if (interceptUrl.origin !== this.#INTERCEPT_ORIGIN) {
|
|
80
|
-
throw new Error(`Invalid intercept URL: ${interceptUrl}`)
|
|
79
|
+
throw new Error(`Invalid intercept URL: ${interceptUrl}`);
|
|
81
80
|
}
|
|
82
|
-
const fileUrl = new URL(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
81
|
+
const fileUrl = new URL(
|
|
82
|
+
interceptUrl.href.slice(this.#INTERCEPT_ORIGIN.length),
|
|
83
|
+
"file://",
|
|
84
|
+
);
|
|
85
|
+
const filePath = await realpath(url.fileURLToPath(fileUrl));
|
|
86
|
+
if (
|
|
87
|
+
![...this.#allowedDirs].some((dir) =>
|
|
88
|
+
path.relative(filePath, dir).startsWith(".."),
|
|
89
|
+
)
|
|
90
|
+
) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Intercept URL is not in an allowed directory: ${interceptUrl}`,
|
|
93
|
+
);
|
|
86
94
|
}
|
|
87
|
-
return fileUrl
|
|
95
|
+
return fileUrl;
|
|
88
96
|
}
|
|
89
97
|
|
|
90
98
|
/**
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
async #interceptRequestHandler
|
|
99
|
+
* @param {import('puppeteer').HTTPRequest} request - The intercepted request
|
|
100
|
+
*/
|
|
101
|
+
async #interceptRequestHandler(request) {
|
|
94
102
|
try {
|
|
95
103
|
if (request.url().startsWith(this.#INTERCEPT_ORIGIN)) {
|
|
96
|
-
const fileUrl = await this.interceptUrlToFileUrl(request.url())
|
|
97
|
-
return request.respond(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
104
|
+
const fileUrl = await this.interceptUrlToFileUrl(request.url());
|
|
105
|
+
return request.respond(
|
|
106
|
+
{
|
|
107
|
+
status: 200,
|
|
108
|
+
headers: {
|
|
109
|
+
"Access-Control-Allow-Origin": "*",
|
|
110
|
+
},
|
|
111
|
+
contentType: getContentTypeFromFileExtension(
|
|
112
|
+
url.fileURLToPath(fileUrl),
|
|
113
|
+
),
|
|
114
|
+
body: await readFile(fileUrl),
|
|
101
115
|
},
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
})
|
|
116
|
+
DEFAULT_INTERCEPT_RESOLUTION_PRIORITY,
|
|
117
|
+
);
|
|
105
118
|
}
|
|
106
119
|
} catch (error) {
|
|
107
|
-
console.error(
|
|
108
|
-
|
|
120
|
+
console.error(
|
|
121
|
+
`Error handling intercept request for ${request.url()}:`,
|
|
122
|
+
error,
|
|
123
|
+
);
|
|
124
|
+
return request.abort(undefined, DEFAULT_INTERCEPT_RESOLUTION_PRIORITY);
|
|
109
125
|
}
|
|
110
|
-
request.continue()
|
|
126
|
+
request.continue(undefined, DEFAULT_INTERCEPT_RESOLUTION_PRIORITY);
|
|
111
127
|
}
|
|
112
128
|
|
|
113
129
|
/**
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
get interceptRequestHandler
|
|
120
|
-
return this.#interceptRequestHandler.bind(this)
|
|
130
|
+
* Intercepts requests to `https://mermaid-cli-intercept.invalid`
|
|
131
|
+
* and serves the corresponding file content.
|
|
132
|
+
*
|
|
133
|
+
* @return {import('puppeteer').Handler<import('puppeteer').HTTPRequest>}
|
|
134
|
+
*/
|
|
135
|
+
get interceptRequestHandler() {
|
|
136
|
+
return this.#interceptRequestHandler.bind(this);
|
|
121
137
|
}
|
|
122
138
|
}
|
package/src/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version =
|
|
1
|
+
export const version = "11.16.0";
|