@jsenv/server 16.1.2 → 16.2.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/dist/client/default_body_4xx_5xx/4xx.html +25 -24
- package/dist/client/route_inspector/route_inspector.html +597 -300
- package/dist/formidable_index/formidable_index.js +4 -4
- package/dist/formidable_index/jsenv_server_node_modules.js +1108 -260
- package/dist/jsenv_server.js +33 -9
- package/dist/ws/ws.js +15 -1
- package/package.json +35 -23
- package/src/cookies/cookie.js +42 -0
- package/src/cookies/cookie_map.js +18 -0
- package/src/interfacing_with_node/from_node_request.js +7 -6
- package/src/router/router.js +5 -3
- package/src/service_controller.js +13 -0
- package/src/services/autoreload_on_server_restart/client/autoreload_on_server_restart.js +25 -24
- package/src/services/open_file/jsenv_service_open_file.js +9 -1
- package/src/services/route_inspector/client/code_block.js +43 -21
- package/src/services/route_inspector/client/route_inspector.html +536 -264
- package/src/start_server.js +1 -1
package/dist/jsenv_server.js
CHANGED
|
@@ -1563,6 +1563,10 @@ const createRequestLogger = (nodeRequest, write) => {
|
|
|
1563
1563
|
write(type, value);
|
|
1564
1564
|
};
|
|
1565
1565
|
const writeLogs = (loggerToWrite, depth) => {
|
|
1566
|
+
const logArray = loggerToWrite.logArray;
|
|
1567
|
+
if (logArray.length === 0) {
|
|
1568
|
+
return;
|
|
1569
|
+
}
|
|
1566
1570
|
let someLogIsError = false;
|
|
1567
1571
|
let someLogIsWarn = false;
|
|
1568
1572
|
for (const log of loggerToWrite.logArray) {
|
|
@@ -1573,12 +1577,9 @@ const createRequestLogger = (nodeRequest, write) => {
|
|
|
1573
1577
|
someLogIsWarn = true;
|
|
1574
1578
|
}
|
|
1575
1579
|
}
|
|
1576
|
-
const firstLog =
|
|
1577
|
-
const lastLog =
|
|
1578
|
-
const middleLogs =
|
|
1579
|
-
if (!firstLog) {
|
|
1580
|
-
debugger;
|
|
1581
|
-
}
|
|
1580
|
+
const firstLog = logArray.shift();
|
|
1581
|
+
const lastLog = logArray.pop();
|
|
1582
|
+
const middleLogs = logArray;
|
|
1582
1583
|
writeLog(firstLog, {
|
|
1583
1584
|
someLogIsError,
|
|
1584
1585
|
someLogIsWarn,
|
|
@@ -3877,6 +3878,8 @@ const createPattern = (
|
|
|
3877
3878
|
namedGroupDelimiter,
|
|
3878
3879
|
prepareStringToGenerate = (stringToBuild) => stringToBuild,
|
|
3879
3880
|
finalizeGeneratedString = (generatedString) => generatedString,
|
|
3881
|
+
// encode = encodeURIComponent,
|
|
3882
|
+
// decode = decodeURIComponent,
|
|
3880
3883
|
} = {},
|
|
3881
3884
|
) => {
|
|
3882
3885
|
if (pattern === "*") {
|
|
@@ -4148,7 +4151,7 @@ const createResourcePattern = (pattern) => {
|
|
|
4148
4151
|
return {
|
|
4149
4152
|
match: (resource) => {
|
|
4150
4153
|
const [pathname, search, hash] = resourceToParts(resource);
|
|
4151
|
-
let result = pathnamePattern.match(pathname);
|
|
4154
|
+
let result = pathnamePattern.match(decodeURIComponent(pathname));
|
|
4152
4155
|
if (!result) {
|
|
4153
4156
|
return null;
|
|
4154
4157
|
}
|
|
@@ -5523,6 +5526,18 @@ const createServiceController = (services) => {
|
|
|
5523
5526
|
}
|
|
5524
5527
|
}
|
|
5525
5528
|
};
|
|
5529
|
+
const callAsyncHooks = async (hookName, info, context, callback) => {
|
|
5530
|
+
const hookSet = hookSetMap.get(hookName);
|
|
5531
|
+
if (!hookSet) {
|
|
5532
|
+
return;
|
|
5533
|
+
}
|
|
5534
|
+
for (const hook of hookSet) {
|
|
5535
|
+
const returnValue = await callAsyncHook(hook, info, context);
|
|
5536
|
+
if (returnValue && callback) {
|
|
5537
|
+
await callback(returnValue, hook.plugin);
|
|
5538
|
+
}
|
|
5539
|
+
}
|
|
5540
|
+
};
|
|
5526
5541
|
const callHooksUntil = (
|
|
5527
5542
|
hookName,
|
|
5528
5543
|
info,
|
|
@@ -5573,6 +5588,7 @@ const createServiceController = (services) => {
|
|
|
5573
5588
|
|
|
5574
5589
|
callHooks,
|
|
5575
5590
|
callHooksUntil,
|
|
5591
|
+
callAsyncHooks,
|
|
5576
5592
|
callAsyncHooksUntil,
|
|
5577
5593
|
|
|
5578
5594
|
getCurrentService: () => currentService,
|
|
@@ -6273,7 +6289,15 @@ const jsenvServiceOpenFile = () => {
|
|
|
6273
6289
|
body: "Missing file in url",
|
|
6274
6290
|
};
|
|
6275
6291
|
}
|
|
6276
|
-
|
|
6292
|
+
let fileUrl;
|
|
6293
|
+
try {
|
|
6294
|
+
fileUrl = new URL(file);
|
|
6295
|
+
} catch {
|
|
6296
|
+
return {
|
|
6297
|
+
status: 400,
|
|
6298
|
+
body: `"${file}" is not a file url`,
|
|
6299
|
+
};
|
|
6300
|
+
}
|
|
6277
6301
|
const filePath = urlToFileSystemPath(fileUrl);
|
|
6278
6302
|
const require = createRequire(import.meta.url);
|
|
6279
6303
|
const launch = require("launch-editor");
|
|
@@ -6965,7 +6989,7 @@ const startServer = async ({
|
|
|
6965
6989
|
);
|
|
6966
6990
|
},
|
|
6967
6991
|
};
|
|
6968
|
-
serviceController.
|
|
6992
|
+
await serviceController.callAsyncHooks(
|
|
6969
6993
|
"augmentRouteFetchSecondArg",
|
|
6970
6994
|
request,
|
|
6971
6995
|
fetchSecondArg,
|
package/dist/ws/ws.js
CHANGED
|
@@ -743,6 +743,14 @@ function requirePermessageDeflate () {
|
|
|
743
743
|
this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH';
|
|
744
744
|
this[kError][kStatusCode] = 1009;
|
|
745
745
|
this.removeListener('data', inflateOnData);
|
|
746
|
+
|
|
747
|
+
//
|
|
748
|
+
// The choice to employ `zlib.reset()` over `zlib.close()` is dictated by the
|
|
749
|
+
// fact that in Node.js versions prior to 13.10.0, the callback for
|
|
750
|
+
// `zlib.flush()` is not called if `zlib.close()` is used. Utilizing
|
|
751
|
+
// `zlib.reset()` ensures that either the callback is invoked or an error is
|
|
752
|
+
// emitted.
|
|
753
|
+
//
|
|
746
754
|
this.reset();
|
|
747
755
|
}
|
|
748
756
|
|
|
@@ -758,6 +766,12 @@ function requirePermessageDeflate () {
|
|
|
758
766
|
// closed when an error is emitted.
|
|
759
767
|
//
|
|
760
768
|
this[kPerMessageDeflate]._inflate = null;
|
|
769
|
+
|
|
770
|
+
if (this[kError]) {
|
|
771
|
+
this[kCallback](this[kError]);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
|
|
761
775
|
err[kStatusCode] = 1007;
|
|
762
776
|
this[kCallback](err);
|
|
763
777
|
}
|
|
@@ -3475,7 +3489,7 @@ function requireWebsocket () {
|
|
|
3475
3489
|
if (parsedUrl.protocol !== 'ws:' && !isSecure && !isIpcUrl) {
|
|
3476
3490
|
invalidUrlMessage =
|
|
3477
3491
|
'The URL\'s protocol must be one of "ws:", "wss:", ' +
|
|
3478
|
-
'"http:", "https", or "ws+unix:"';
|
|
3492
|
+
'"http:", "https:", or "ws+unix:"';
|
|
3479
3493
|
} else if (isIpcUrl && !parsedUrl.pathname) {
|
|
3480
3494
|
invalidUrlMessage = "The URL's pathname is empty";
|
|
3481
3495
|
} else if (parsedUrl.hash) {
|
package/package.json
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/server",
|
|
3
|
-
"version": "16.
|
|
3
|
+
"version": "16.2.0",
|
|
4
4
|
"description": "A modern Node.js HTTP server with declarative routing, content negotiation, and WebSocket support",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": {
|
|
7
|
-
"name": "dmail",
|
|
8
|
-
"email": "dmaillard06@gmail.com"
|
|
9
|
-
},
|
|
10
5
|
"repository": {
|
|
11
6
|
"type": "git",
|
|
12
7
|
"url": "https://github.com/jsenv/core",
|
|
13
|
-
"directory": "packages/
|
|
8
|
+
"directory": "packages/backend/server"
|
|
14
9
|
},
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"access": "public"
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"author": {
|
|
12
|
+
"name": "dmail",
|
|
13
|
+
"email": "dmaillard06@gmail.com"
|
|
20
14
|
},
|
|
15
|
+
"sideEffects": [
|
|
16
|
+
"./dist/jsenv_server.js"
|
|
17
|
+
],
|
|
21
18
|
"type": "module",
|
|
22
19
|
"exports": {
|
|
23
20
|
".": {
|
|
@@ -33,22 +30,37 @@
|
|
|
33
30
|
"/dist/",
|
|
34
31
|
"/src/"
|
|
35
32
|
],
|
|
36
|
-
"sideEffects": [
|
|
37
|
-
"./dist/jsenv_server_node_modules.js"
|
|
38
|
-
],
|
|
39
33
|
"scripts": {
|
|
40
34
|
"build": "node --experimental-import-meta-resolve ./scripts/build.mjs",
|
|
41
35
|
"prepublishOnly": "npm run build"
|
|
42
36
|
},
|
|
43
37
|
"devDependencies": {
|
|
44
|
-
"
|
|
45
|
-
"@jsenv/
|
|
46
|
-
"@jsenv/
|
|
47
|
-
"@jsenv/
|
|
48
|
-
"@jsenv/
|
|
49
|
-
"
|
|
50
|
-
"
|
|
38
|
+
"@jsenv/abort": "../../tooling/abort",
|
|
39
|
+
"@jsenv/assert": "../../tooling/assert",
|
|
40
|
+
"@jsenv/core": "../../../",
|
|
41
|
+
"@jsenv/fetch": "../../tooling/fetch",
|
|
42
|
+
"@jsenv/filesystem": "../../tooling/filesystem",
|
|
43
|
+
"@jsenv/https-local": "../../tooling/https-local",
|
|
44
|
+
"@jsenv/humanize": "../../tooling/humanize",
|
|
45
|
+
"@jsenv/plugin-commonjs": "../../related/plugin-commonjs",
|
|
46
|
+
"@jsenv/router": "../../frontend/router",
|
|
47
|
+
"@jsenv/server": "./",
|
|
48
|
+
"@jsenv/snapshot": "../../tooling/snapshot",
|
|
49
|
+
"@jsenv/url-meta": "../../tooling/url_meta",
|
|
50
|
+
"@jsenv/url-pattern": "../../tooling/url-pattern",
|
|
51
|
+
"@jsenv/urls": "../../tooling/urls",
|
|
52
|
+
"@jsenv/utils": "../../tooling/utils",
|
|
51
53
|
"eventsource": "3.0.6",
|
|
52
|
-
"
|
|
54
|
+
"formidable": "3.5.4",
|
|
55
|
+
"launch-editor": "2.10.0",
|
|
56
|
+
"playwright": "1.52.0",
|
|
57
|
+
"undici": "7.8.0",
|
|
58
|
+
"ws": "8.18.2"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=22.13.1"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public"
|
|
53
65
|
}
|
|
54
66
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// https://bun.sh/docs/api/cookie
|
|
2
|
+
|
|
3
|
+
export const COOKIE = {
|
|
4
|
+
from: (name, value, options = {}) => {
|
|
5
|
+
return { name, value, options };
|
|
6
|
+
},
|
|
7
|
+
parse: (cookieString) => {
|
|
8
|
+
const parts = cookieString.split("; ");
|
|
9
|
+
const [nameValue, ...attributes] = parts;
|
|
10
|
+
const [name, value] = nameValue.split("=");
|
|
11
|
+
const options = {};
|
|
12
|
+
for (const attr of attributes) {
|
|
13
|
+
const [key, val] = attr.split("=");
|
|
14
|
+
if (key === "expires") {
|
|
15
|
+
options.expires = new Date(val);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (key === "path") {
|
|
20
|
+
options.path = val;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (key === "domain") {
|
|
24
|
+
options.domain = val;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (key === "secure") {
|
|
28
|
+
options.secure = true;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (key === "httponly") {
|
|
32
|
+
options.httpOnly = true;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return { name, value, options };
|
|
38
|
+
},
|
|
39
|
+
stringify: ({ name, value, options }) => {
|
|
40
|
+
return `${name}=${value}${options.expires ? `; Expires=${options.expires.toUTCString()}` : ""}${options.path ? `; Path=${options.path}` : ""}${options.domain ? `; Domain=${options.domain}` : ""}${options.secure ? "; Secure" : ""}${options.httpOnly ? "; HttpOnly" : ""}`;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { COOKIE } from "./cookie.js";
|
|
2
|
+
|
|
3
|
+
export const createCookieMap = (setCookieString) => {
|
|
4
|
+
const map = new Map();
|
|
5
|
+
|
|
6
|
+
if (setCookieString) {
|
|
7
|
+
const cookies = setCookieString.split(", ");
|
|
8
|
+
for (const cookie of cookies) {
|
|
9
|
+
const parsedCookie = COOKIE.parse(cookie);
|
|
10
|
+
map.set(parsedCookie.name, parsedCookie);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
map,
|
|
16
|
+
toSetCookieHeaders: () => {},
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -290,6 +290,10 @@ const createRequestLogger = (nodeRequest, write) => {
|
|
|
290
290
|
write(type, value);
|
|
291
291
|
};
|
|
292
292
|
const writeLogs = (loggerToWrite, depth) => {
|
|
293
|
+
const logArray = loggerToWrite.logArray;
|
|
294
|
+
if (logArray.length === 0) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
293
297
|
let someLogIsError = false;
|
|
294
298
|
let someLogIsWarn = false;
|
|
295
299
|
for (const log of loggerToWrite.logArray) {
|
|
@@ -300,12 +304,9 @@ const createRequestLogger = (nodeRequest, write) => {
|
|
|
300
304
|
someLogIsWarn = true;
|
|
301
305
|
}
|
|
302
306
|
}
|
|
303
|
-
const firstLog =
|
|
304
|
-
const lastLog =
|
|
305
|
-
const middleLogs =
|
|
306
|
-
if (!firstLog) {
|
|
307
|
-
debugger;
|
|
308
|
-
}
|
|
307
|
+
const firstLog = logArray.shift();
|
|
308
|
+
const lastLog = logArray.pop();
|
|
309
|
+
const middleLogs = logArray;
|
|
309
310
|
writeLog(firstLog, {
|
|
310
311
|
someLogIsError,
|
|
311
312
|
someLogIsWarn,
|
package/src/router/router.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { parseFunction } from "@jsenv/assert/src/utils/function_parser.js";
|
|
2
2
|
import { lookupPackageDirectory } from "@jsenv/filesystem";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
createHeadersPattern,
|
|
5
|
+
createResourcePattern,
|
|
6
|
+
PATTERN,
|
|
7
|
+
} from "@jsenv/url-pattern";
|
|
6
8
|
import { resourceToExtension, urlToRelativeUrl } from "@jsenv/urls";
|
|
7
9
|
import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
|
|
8
10
|
import { readFileSync } from "node:fs";
|
|
@@ -89,6 +89,18 @@ export const createServiceController = (services) => {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
|
+
const callAsyncHooks = async (hookName, info, context, callback) => {
|
|
93
|
+
const hookSet = hookSetMap.get(hookName);
|
|
94
|
+
if (!hookSet) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
for (const hook of hookSet) {
|
|
98
|
+
const returnValue = await callAsyncHook(hook, info, context);
|
|
99
|
+
if (returnValue && callback) {
|
|
100
|
+
await callback(returnValue, hook.plugin);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
92
104
|
const callHooksUntil = (
|
|
93
105
|
hookName,
|
|
94
106
|
info,
|
|
@@ -139,6 +151,7 @@ export const createServiceController = (services) => {
|
|
|
139
151
|
|
|
140
152
|
callHooks,
|
|
141
153
|
callHooksUntil,
|
|
154
|
+
callAsyncHooks,
|
|
142
155
|
callAsyncHooksUntil,
|
|
143
156
|
|
|
144
157
|
getCurrentService: () => currentService,
|
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
const css = /* css */ `
|
|
2
|
-
dialog {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
dialog::backdrop {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
dialog:open {
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
dialog:open::backdrop {
|
|
21
|
-
|
|
22
|
-
}
|
|
2
|
+
dialog {
|
|
3
|
+
opacity: 0;
|
|
4
|
+
transition: all 0.3s allow-discrete;
|
|
5
|
+
}
|
|
6
|
+
dialog::backdrop {
|
|
7
|
+
background-image: linear-gradient(
|
|
8
|
+
45deg,
|
|
9
|
+
magenta,
|
|
10
|
+
rebeccapurple,
|
|
11
|
+
dodgerblue,
|
|
12
|
+
green
|
|
13
|
+
);
|
|
14
|
+
opacity: 0;
|
|
15
|
+
transition: all 0.3s allow-discrete;
|
|
16
|
+
}
|
|
17
|
+
dialog:open {
|
|
18
|
+
opacity: 1;
|
|
19
|
+
}
|
|
20
|
+
dialog:open::backdrop {
|
|
21
|
+
opacity: 0.75;
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
23
24
|
|
|
24
25
|
const html = /* html */ `<style>
|
|
25
26
|
${css}
|
|
26
|
-
</style>
|
|
27
|
-
<dialog>
|
|
27
|
+
</style>
|
|
28
|
+
<dialog>
|
|
28
29
|
<p>Connection with server is lost</p>
|
|
29
30
|
<button autofocus name="reconnect">Retry</button>
|
|
30
|
-
</dialog>`;
|
|
31
|
+
</dialog>`;
|
|
31
32
|
|
|
32
33
|
class JsenvAutoreloadOnServerRestart extends HTMLElement {
|
|
33
34
|
constructor({ url } = {}) {
|
|
@@ -17,7 +17,15 @@ export const jsenvServiceOpenFile = () => {
|
|
|
17
17
|
body: "Missing file in url",
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
let fileUrl;
|
|
21
|
+
try {
|
|
22
|
+
fileUrl = new URL(file);
|
|
23
|
+
} catch {
|
|
24
|
+
return {
|
|
25
|
+
status: 400,
|
|
26
|
+
body: `"${file}" is not a file url`,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
21
29
|
const filePath = urlToFileSystemPath(fileUrl);
|
|
22
30
|
const require = createRequire(import.meta.url);
|
|
23
31
|
const launch = require("launch-editor");
|
|
@@ -47,19 +47,38 @@ button:hover {
|
|
|
47
47
|
`;
|
|
48
48
|
|
|
49
49
|
const html = /* html */ `<style>
|
|
50
|
-
${css}
|
|
51
|
-
</style>
|
|
52
|
-
<div class="clipboard_container">
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
${css}
|
|
51
|
+
</style>
|
|
52
|
+
<div class="clipboard_container">
|
|
53
|
+
<div id="copied_notif" aria-hidden="true">Copied !</div>
|
|
54
|
+
<button id="copy_button">
|
|
55
|
+
<svg
|
|
56
|
+
id="copy_icon"
|
|
57
|
+
aria-hidden="true"
|
|
58
|
+
viewBox="0 0 16 16"
|
|
59
|
+
width="16"
|
|
60
|
+
height="16"
|
|
61
|
+
>
|
|
62
|
+
<path
|
|
63
|
+
d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"
|
|
64
|
+
></path>
|
|
65
|
+
<path
|
|
66
|
+
d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"
|
|
67
|
+
></path>
|
|
68
|
+
</svg>
|
|
69
|
+
<svg
|
|
70
|
+
id="copied_icon"
|
|
71
|
+
aria-hidden="true"
|
|
72
|
+
viewBox="0 0 16 16"
|
|
73
|
+
width="16"
|
|
74
|
+
height="16"
|
|
75
|
+
>
|
|
76
|
+
<path
|
|
77
|
+
d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"
|
|
78
|
+
></path>
|
|
79
|
+
</svg>
|
|
80
|
+
</button>
|
|
81
|
+
</div>`;
|
|
63
82
|
class ClipboardCopy extends HTMLElement {
|
|
64
83
|
constructor() {
|
|
65
84
|
super();
|
|
@@ -130,14 +149,17 @@ ${css}
|
|
|
130
149
|
`;
|
|
131
150
|
|
|
132
151
|
const html = /* html */ `<style>
|
|
133
|
-
${css}
|
|
134
|
-
</style>
|
|
135
|
-
<div id="code_block">
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
152
|
+
${css}
|
|
153
|
+
</style>
|
|
154
|
+
<div id="code_block">
|
|
155
|
+
<pre id="pre"><code></code></pre>
|
|
156
|
+
<div
|
|
157
|
+
id="clipboard_copy_container"
|
|
158
|
+
style="position: absolute; right: 0; top: 0"
|
|
159
|
+
>
|
|
160
|
+
<clipboard-copy></clipboard-copy>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
141
163
|
`;
|
|
142
164
|
|
|
143
165
|
let loadPromise;
|