mirror of
https://github.com/donlon/cloudflare-error-page.git
synced 2025-12-19 14:59:28 +00:00
editor: migrate frontend project to npm
This commit is contained in:
9
editor/frontend/.prettierrc
Normal file
9
editor/frontend/.prettierrc
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"endOfLine": "lf",
|
||||||
|
"printWidth": 120,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"useTabs": false
|
||||||
|
}
|
||||||
429
editor/frontend/index.html
Normal file
429
editor/frontend/index.html
Normal file
@@ -0,0 +1,429 @@
|
|||||||
|
<!-- SPDX-License-Identifier: MIT -->
|
||||||
|
<!--
|
||||||
|
!!!
|
||||||
|
!!! Note: This file is vibely generated, and could be very hard to maintain.
|
||||||
|
!!!
|
||||||
|
-->
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>Cloudflare Error Page Editor</title>
|
||||||
|
|
||||||
|
<meta name="description" content="Online editor to create customized Cloudflare-styled error pages.">
|
||||||
|
<meta name="keywords" content="cloudflare,error,page,editor">
|
||||||
|
<link rel="canonical" href="https://virt.moe/cloudflare-error-page/editor/" />
|
||||||
|
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:site_name" content="moe::virt" />
|
||||||
|
<meta property="og:title" content="Cloudflare error page editor" />
|
||||||
|
<meta property="og:url" content="https://virt.moe/cloudflare-error-page/editor/" />
|
||||||
|
<meta property="og:description" content="Online editor to create customized Cloudflare-styled error pages" />
|
||||||
|
|
||||||
|
<meta property="twitter:card" content="summary" />
|
||||||
|
<meta property="twitter:site" content="moe::virt" />
|
||||||
|
<meta property="twitter:title" content="Cloudflare error page editor" />
|
||||||
|
<meta property="twitter:description" content="Online editor to create customized Cloudflare-styled error pages" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
|
|
||||||
|
<link href="https://virt.moe/assets/cloudflare-error-page/bootstrap.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<script type="module" src="src/index.js"></script>
|
||||||
|
<style>
|
||||||
|
/* Layout: editor + preview */
|
||||||
|
.app {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
height: 100vh;
|
||||||
|
/* padding: 0.8rem; */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* On md and up, arrange horizontally: editor left, preview right */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.app {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor {
|
||||||
|
flex: 0 0;
|
||||||
|
min-width: 380px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
flex: 1 1 48%;
|
||||||
|
/* max-width: 48%; */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
iframe.preview-frame {
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* On small screens use stacked layout: editor then iframe */
|
||||||
|
.editor {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3e6ea;
|
||||||
|
border-radius: .5rem;
|
||||||
|
padding: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e3e6ea;
|
||||||
|
border-radius: .5rem;
|
||||||
|
padding: 0.8rem 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact form: label and control same row */
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
gap: .3rem;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: .6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row>label {
|
||||||
|
min-width: 6rem;
|
||||||
|
max-width: 12rem;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row>.control {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status block styling */
|
||||||
|
.status-block {
|
||||||
|
border: 1px solid #cfeadd;
|
||||||
|
border-radius: .375rem;
|
||||||
|
padding: .75rem;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-ok {
|
||||||
|
background: #e9f7ef;
|
||||||
|
border: 1px solid #cfeadd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-error {
|
||||||
|
background: #fff5f5;
|
||||||
|
border: 1px solid #f3c2c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iframe styling */
|
||||||
|
iframe.preview-frame {
|
||||||
|
--expanded-height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
height: var(--expanded-height);
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-height: 360px;
|
||||||
|
border-radius: .375rem;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, .06);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Controls toolbar */
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: .5rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact textarea resizing */
|
||||||
|
textarea.compact {
|
||||||
|
min-height: 80px;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="bg-light">
|
||||||
|
|
||||||
|
<div class="container-fluid h-100">
|
||||||
|
<div class="app">
|
||||||
|
|
||||||
|
<!-- Editor column -->
|
||||||
|
<div class="editor">
|
||||||
|
<h5 class="form-row">Cloudflare Error Page Editor</h5>
|
||||||
|
<hr>
|
||||||
|
<div class="form-row mb-3">
|
||||||
|
<label for="presetSelect">Preset</label>
|
||||||
|
<select id="presetSelect" class="form-select form-select-sm">
|
||||||
|
<option value="default">Internal server error (Default)</option>
|
||||||
|
<option value="empty">Empty</option>
|
||||||
|
<option value="catastrophic">Catastrophic failure</option>
|
||||||
|
<option value="working">Server working</option>
|
||||||
|
<option value="consensual">Myth of consensual</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<form id="editorForm" class="needs-validation" novalidate>
|
||||||
|
<!-- Basic properties -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<!-- <h6 class="mb-2">Page</h6> -->
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="title">Title</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="title" class="form-control form-control-sm" placeholder="Internal server error" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="error_code">Error Code</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="error_code" class="form-control form-control-sm" placeholder="500" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<!-- Status blocks -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<h6 class="mb-2">Status</h6>
|
||||||
|
|
||||||
|
<!-- Browser -->
|
||||||
|
<div id="block_browser" class="status-block status-ok">
|
||||||
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||||
|
<strong>Browser</strong>
|
||||||
|
<div>
|
||||||
|
<input class="form-check-input" type="radio" name="error_source" id="err_browser" value="browser" />
|
||||||
|
<label for="err_browser" class="ms-1 small">Error here</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="browser_status">Status</label>
|
||||||
|
<div class="control">
|
||||||
|
<select id="browser_status" class="form-select form-select-sm">
|
||||||
|
<option value="ok">Ok</option>
|
||||||
|
<option value="error">Error</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="browser_location">Location</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="browser_location" class="form-control form-control-sm" placeholder="You" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="browser_name">Name</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="browser_name" class="form-control form-control-sm" placeholder="Browser" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="browser_status_text">Status Text</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="browser_status_text" class="form-control form-control-sm" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Cloudflare -->
|
||||||
|
<div id="block_cloudflare" class="status-block status-error">
|
||||||
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||||
|
<strong>Cloudflare</strong>
|
||||||
|
<div>
|
||||||
|
<input class="form-check-input" type="radio" name="error_source" id="err_cloudflare"
|
||||||
|
value="cloudflare" />
|
||||||
|
<label for="err_cloudflare" class="ms-1 small">Error here</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="cloudflare_status">Status</label>
|
||||||
|
<div class="control">
|
||||||
|
<select id="cloudflare_status" class="form-select form-select-sm">
|
||||||
|
<option value="ok">Ok</option>
|
||||||
|
<option value="error">Error</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="cloudflare_location">Location</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="cloudflare_location" class="form-control form-control-sm" placeholder="San Francisco" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="cloudflare_name">Name</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="cloudflare_name" class="form-control form-control-sm" placeholder="Cloudflare" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="cloudflare_status_text">Status Text</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="cloudflare_status_text" class="form-control form-control-sm" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Host -->
|
||||||
|
<div id="block_host" class="status-block status-ok">
|
||||||
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||||
|
<strong>Host</strong>
|
||||||
|
<div>
|
||||||
|
<input class="form-check-input" type="radio" name="error_source" id="err_host" value="host" />
|
||||||
|
<label for="err_host" class="ms-1 small">Error here</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="host_status">Status</label>
|
||||||
|
<div class="control">
|
||||||
|
<select id="host_status" class="form-select form-select-sm">
|
||||||
|
<option value="ok">Ok</option>
|
||||||
|
<option value="error">Error</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="host_location">Location</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="host_location" class="form-control form-control-sm" placeholder="Website" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="host_name">Name</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="host_name" class="form-control form-control-sm" placeholder="Host" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="host_status_text">Status Text</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="host_status_text" class="form-control form-control-sm" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="status-block mt-3 mb-3">
|
||||||
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||||
|
<strong>Visit ...</strong>
|
||||||
|
<div>
|
||||||
|
<input id="more_hidden" class="form-check-input" type="checkbox" />
|
||||||
|
<label for="more_hidden" class="ms-1 small">Hidden</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="more_text">Text</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="more_text" class="form-control form-control-sm" placeholder="cloudflare.com" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="more_link">Link</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="more_link" class="form-control form-control-sm" placeholder="https://www.cloudflare.com/" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="more_for">For</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="more_for" class="form-control form-control-sm" placeholder="more information" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label for="what_happened" class="fw-semibold">What happened?</label>
|
||||||
|
<div class="control">
|
||||||
|
<textarea id="what_happened" class="form-control compact"
|
||||||
|
placeholder="There is an internal server error on Cloudflare's network."></textarea>
|
||||||
|
</div>
|
||||||
|
<!-- </div> -->
|
||||||
|
|
||||||
|
<!-- <div class=""> -->
|
||||||
|
<label for="what_can_i_do" class="fw-semibold mt-2">What can I do?</label>
|
||||||
|
<div class="control">
|
||||||
|
<textarea id="what_can_i_do" class="form-control compact"
|
||||||
|
placeholder="Please try again in a few minutes."></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h6 class="form-row">Performance & security by ...</h6>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="perf_text">Text</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="perf_text" class="form-control form-control-sm" placeholder="Cloudflare" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="perf_link">Link</label>
|
||||||
|
<div class="control">
|
||||||
|
<input id="perf_link" class="form-control form-control-sm" placeholder="https://www.cloudflare.com/" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- </div> -->
|
||||||
|
|
||||||
|
<div class="d-flex gap-2 mt-2 mb-2">
|
||||||
|
<!-- <button id="btnRender" class="btn btn-sm btn-primary">Render</button> -->
|
||||||
|
<button id="btnOpen" class="btn btn-sm btn-primary">Preview in new tab</button>
|
||||||
|
<button id="btnExport" class="btn btn-sm btn-primary">Export JSON</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="btnShare" class="btn btn-sm btn-primary">Create shareable link</button>
|
||||||
|
<div class="mt-2">
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<input id="shareLink" class="form-control" readonly />
|
||||||
|
<button id="btnCopyLink" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2 text-center">>> Star this project on
|
||||||
|
<a href="https://github.com/donlon/cloudflare-error-page" target="_blank">GitHub</a> ⭐
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2" style="font-size: 0.9em;">You can also embed this error page into your own website. See
|
||||||
|
<a href="https://github.com/donlon/cloudflare-error-page#quickstart-for-programmers"
|
||||||
|
target="_blank">Quickstart</a> in the
|
||||||
|
homepage for steps.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Preview column -->
|
||||||
|
<div class="preview">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-1" style="padding: 0 0.8em;">
|
||||||
|
<h6><strong>Preview</strong></h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- TODO: An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing. -->
|
||||||
|
<iframe id="previewFrame" class="preview-frame" sandbox="allow-scripts allow-same-origin"></iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
22
editor/frontend/package.json
Normal file
22
editor/frontend/package.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "cloudflare-error-page-editor",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"format": "prettier src --write",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"preview": "npm run build && vite preview"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"html-minifier-terser": "^7.2.0",
|
||||||
|
"prettier": "3.7.4",
|
||||||
|
"typescript": "^5.9.3",
|
||||||
|
"vite": "^7.2.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bootstrap": "^5.3.8",
|
||||||
|
"ejs": "^3.1.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
438
editor/frontend/src/index.js
Normal file
438
editor/frontend/src/index.js
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
/*
|
||||||
|
Editor logic
|
||||||
|
- initialConfig: provided default config (from your JSON)
|
||||||
|
- render(): placeholder that generates HTML and writes to iframe.srcdoc
|
||||||
|
- inputs call render() on change
|
||||||
|
- "Open in new tab" opens the rendered HTML in a new window using a blob URL
|
||||||
|
*/
|
||||||
|
import ejs from 'ejs'
|
||||||
|
import templateContent from './template.ejs?raw'
|
||||||
|
|
||||||
|
|
||||||
|
let template = ejs.compile(templateContent);
|
||||||
|
|
||||||
|
// can be changed if specified by '?from=<name>'
|
||||||
|
let initialConfig = {
|
||||||
|
title: 'Internal server error',
|
||||||
|
error_code: '500',
|
||||||
|
more_information: {
|
||||||
|
hidden: false,
|
||||||
|
text: 'cloudflare.com',
|
||||||
|
for: 'more information',
|
||||||
|
},
|
||||||
|
browser_status: {
|
||||||
|
status: 'ok',
|
||||||
|
location: 'You',
|
||||||
|
name: 'Browser',
|
||||||
|
status_text: 'Working',
|
||||||
|
},
|
||||||
|
cloudflare_status: {
|
||||||
|
status: 'error',
|
||||||
|
location: 'San Francisco',
|
||||||
|
name: 'Cloudflare',
|
||||||
|
status_text: 'Error',
|
||||||
|
},
|
||||||
|
host_status: {
|
||||||
|
status: 'ok',
|
||||||
|
location: 'Website',
|
||||||
|
name: 'Host',
|
||||||
|
status_text: 'Working',
|
||||||
|
},
|
||||||
|
error_source: 'cloudflare',
|
||||||
|
what_happened: "There is an internal server error on Cloudflare's network.",
|
||||||
|
what_can_i_do: 'Please try again in a few minutes.',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Demo presets (content brief — replace or expand as needed)
|
||||||
|
const PRESETS = {
|
||||||
|
default: structuredClone(initialConfig),
|
||||||
|
empty: {
|
||||||
|
error_code: '500',
|
||||||
|
},
|
||||||
|
catastrophic: {
|
||||||
|
title: 'Catastrophic infrastructure failure',
|
||||||
|
error_code: '500',
|
||||||
|
more_information: {
|
||||||
|
for: 'no information',
|
||||||
|
},
|
||||||
|
browser_status: {
|
||||||
|
status: 'error',
|
||||||
|
status_text: 'Out of Memory',
|
||||||
|
},
|
||||||
|
cloudflare_status: {
|
||||||
|
status: 'error',
|
||||||
|
location: 'Everywhere',
|
||||||
|
status_text: 'Error',
|
||||||
|
},
|
||||||
|
host_status: {
|
||||||
|
status: 'error',
|
||||||
|
location: 'example.com',
|
||||||
|
status_text: 'On Fire',
|
||||||
|
},
|
||||||
|
error_source: 'cloudflare',
|
||||||
|
what_happened: 'There is a catastrophic failure.',
|
||||||
|
what_can_i_do: 'Please try again in a few years.',
|
||||||
|
},
|
||||||
|
working: {
|
||||||
|
title: 'Web server is working',
|
||||||
|
error_code: '200',
|
||||||
|
more_information: {
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
browser_status: {
|
||||||
|
status: 'ok',
|
||||||
|
status_text: 'Seems Working',
|
||||||
|
},
|
||||||
|
cloudflare_status: {
|
||||||
|
status: 'ok',
|
||||||
|
status_text: 'Often Working',
|
||||||
|
},
|
||||||
|
host_status: {
|
||||||
|
status: 'ok',
|
||||||
|
location: 'example.com',
|
||||||
|
status_text: 'Almost Working',
|
||||||
|
},
|
||||||
|
error_source: 'host',
|
||||||
|
what_happened: 'This site is still working. And it looks great.',
|
||||||
|
what_can_i_do: 'Visit the site before it crashes someday.',
|
||||||
|
},
|
||||||
|
consensual: {
|
||||||
|
title: 'The Myth Of "Consensual" Internet',
|
||||||
|
error_code: 'lmao',
|
||||||
|
more_information: {
|
||||||
|
hidden: false,
|
||||||
|
text: 'r/ProgrammerHumor',
|
||||||
|
link: 'https://redd.it/1p2yola',
|
||||||
|
},
|
||||||
|
browser_status: {
|
||||||
|
status: 'ok',
|
||||||
|
location: 'You',
|
||||||
|
name: 'Browser',
|
||||||
|
status_text: 'I Consent',
|
||||||
|
},
|
||||||
|
cloudflare_status: {
|
||||||
|
status: 'error',
|
||||||
|
location: 'F***ing Everywhere',
|
||||||
|
name: 'Cloudflare',
|
||||||
|
status_text: "I Don't!",
|
||||||
|
},
|
||||||
|
host_status: {
|
||||||
|
status: 'ok',
|
||||||
|
location: 'Remote',
|
||||||
|
name: 'Host',
|
||||||
|
status_text: 'I Consent',
|
||||||
|
},
|
||||||
|
error_source: 'cloudflare',
|
||||||
|
what_happened: "Isn't There Someone You Forgot To Ask?",
|
||||||
|
what_can_i_do: 'Kill Yourself',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function extractUrlParam(str, key) {
|
||||||
|
const urlParams = new URLSearchParams(str);
|
||||||
|
return urlParams.get(key);
|
||||||
|
}
|
||||||
|
function getDefaultPresetName() {
|
||||||
|
const key = 'from';
|
||||||
|
let name = extractUrlParam(window.location.search, key);
|
||||||
|
if (!name) {
|
||||||
|
name = extractUrlParam(window.location.hash.substr(1), key);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
const defaultPresetName = getDefaultPresetName();
|
||||||
|
if (defaultPresetName && defaultPresetName.indexOf('/') < 0) {
|
||||||
|
fetch(`../s/${defaultPresetName}`, {
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('failed to get preset');
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
if (result.status != 'ok') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(result.parameters);
|
||||||
|
initialConfig = result.parameters;
|
||||||
|
loadConfig(initialConfig);
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Utilities */
|
||||||
|
function $(id) {
|
||||||
|
return document.getElementById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill form from config */
|
||||||
|
function loadConfig(cfg) {
|
||||||
|
$('title').value = cfg.title ?? '';
|
||||||
|
$('error_code').value = cfg.error_code ?? '';
|
||||||
|
|
||||||
|
$('more_hidden').checked = !!(cfg.more_information && cfg.more_information.hidden);
|
||||||
|
$('more_text').value = cfg.more_information?.text ?? '';
|
||||||
|
$('more_link').value = cfg.more_information?.link ?? '';
|
||||||
|
$('more_for').value = cfg.more_information?.for ?? '';
|
||||||
|
|
||||||
|
$('browser_status').value = cfg.browser_status?.status ?? 'ok';
|
||||||
|
$('browser_location').value = cfg.browser_status?.location ?? '';
|
||||||
|
$('browser_name').value = cfg.browser_status?.name ?? '';
|
||||||
|
$('browser_status_text').value = cfg.browser_status?.status_text ?? '';
|
||||||
|
|
||||||
|
$('cloudflare_status').value = cfg.cloudflare_status?.status ?? 'ok';
|
||||||
|
$('cloudflare_location').value = cfg.cloudflare_status?.location ?? '';
|
||||||
|
$('cloudflare_name').value = cfg.cloudflare_status?.name ?? '';
|
||||||
|
$('cloudflare_status_text').value = cfg.cloudflare_status?.status_text ?? '';
|
||||||
|
|
||||||
|
$('host_status').value = cfg.host_status?.status ?? 'ok';
|
||||||
|
$('host_location').value = cfg.host_status?.location ?? '';
|
||||||
|
$('host_name').value = cfg.host_status?.name ?? '';
|
||||||
|
$('host_status_text').value = cfg.host_status?.status_text ?? '';
|
||||||
|
|
||||||
|
if (cfg.error_source === 'browser') $('err_browser').checked = true;
|
||||||
|
else if (cfg.error_source === 'cloudflare') $('err_cloudflare').checked = true;
|
||||||
|
else $('err_host').checked = true;
|
||||||
|
|
||||||
|
$('what_happened').value = cfg.what_happened ?? '';
|
||||||
|
$('what_can_i_do').value = cfg.what_can_i_do ?? '';
|
||||||
|
|
||||||
|
$('perf_text').value = cfg.perf_sec_by?.text ?? '';
|
||||||
|
$('perf_link').value = cfg.perf_sec_by?.link ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read config from form inputs */
|
||||||
|
function readConfig() {
|
||||||
|
return {
|
||||||
|
title: $('title').value,
|
||||||
|
error_code: $('error_code').value,
|
||||||
|
more_information: {
|
||||||
|
hidden: !!$('more_hidden').checked,
|
||||||
|
text: $('more_text').value,
|
||||||
|
link: $('more_link').value,
|
||||||
|
for: $('more_for').value,
|
||||||
|
},
|
||||||
|
browser_status: {
|
||||||
|
status: $('browser_status').value,
|
||||||
|
location: $('browser_location').value,
|
||||||
|
name: $('browser_name').value,
|
||||||
|
status_text: $('browser_status_text').value,
|
||||||
|
},
|
||||||
|
cloudflare_status: {
|
||||||
|
status: $('cloudflare_status').value,
|
||||||
|
location: $('cloudflare_location').value,
|
||||||
|
name: $('cloudflare_name').value,
|
||||||
|
status_text: $('cloudflare_status_text').value,
|
||||||
|
},
|
||||||
|
host_status: {
|
||||||
|
status: $('host_status').value,
|
||||||
|
location: $('host_location').value,
|
||||||
|
name: $('host_name').value,
|
||||||
|
status_text: $('host_status_text').value,
|
||||||
|
},
|
||||||
|
error_source: (
|
||||||
|
document.querySelector('input[name="error_source"]:checked') || {
|
||||||
|
value: 'host',
|
||||||
|
}
|
||||||
|
).value,
|
||||||
|
what_happened: $('what_happened').value,
|
||||||
|
what_can_i_do: $('what_can_i_do').value,
|
||||||
|
perf_sec_by: {
|
||||||
|
text: $('perf_text').value,
|
||||||
|
link: $('perf_link').value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function formatUtcTimestamp() {
|
||||||
|
const d = new Date();
|
||||||
|
|
||||||
|
const year = d.getUTCFullYear();
|
||||||
|
const month = String(d.getUTCMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(d.getUTCDate()).padStart(2, '0');
|
||||||
|
|
||||||
|
const hours = String(d.getUTCHours()).padStart(2, '0');
|
||||||
|
const minutes = String(d.getUTCMinutes()).padStart(2, '0');
|
||||||
|
const seconds = String(d.getUTCSeconds()).padStart(2, '0');
|
||||||
|
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} UTC`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderEjs(params) {
|
||||||
|
return template({
|
||||||
|
params: params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Basic render: build HTML string from config and put into iframe.srcdoc */
|
||||||
|
function render() {
|
||||||
|
const cfg = readConfig();
|
||||||
|
window.lastCfg = cfg;
|
||||||
|
|
||||||
|
cfg.time = formatUtcTimestamp();
|
||||||
|
cfg.ray_id = '0123456789abcdef';
|
||||||
|
cfg.client_ip = '1.1.1.1';
|
||||||
|
if (Number.isNaN(Number(cfg.error_code))) {
|
||||||
|
cfg.html_title = cfg.title || 'Internal server error';
|
||||||
|
}
|
||||||
|
|
||||||
|
let pageHtml = renderEjs(cfg);
|
||||||
|
// Write into iframe
|
||||||
|
const iframe = $('previewFrame');
|
||||||
|
let doc = iframe.contentDocument;
|
||||||
|
doc.open();
|
||||||
|
doc.write(pageHtml);
|
||||||
|
doc.close();
|
||||||
|
|
||||||
|
updateStatusBlockStyles();
|
||||||
|
|
||||||
|
// store last rendered HTML for "open in new tab"
|
||||||
|
lastRenderedHtml = pageHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open in new tab: create blob and open */
|
||||||
|
let lastRenderedHtml = '';
|
||||||
|
function openInNewTab() {
|
||||||
|
if (!lastRenderedHtml) render();
|
||||||
|
const blob = new Blob([lastRenderedHtml], { type: 'text/html' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
window.open(url, '_blank', 'noopener');
|
||||||
|
// note that this url won't be revoked
|
||||||
|
}
|
||||||
|
|
||||||
|
function createShareableLink() {
|
||||||
|
$('shareLink').value = '';
|
||||||
|
fetch('../s/create', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
parameters: window.lastCfg,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
alert('failed to create link');
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
if (result.status != 'ok') {
|
||||||
|
alert('failed to create link');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('shareLink').value = result.url;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function exportJSON() {
|
||||||
|
let content = JSON.stringify(lastCfg, null, 4);
|
||||||
|
const file = new File([content], 'cloudflare-error-page-params.json', {
|
||||||
|
type: 'text/plain',
|
||||||
|
});
|
||||||
|
const url = URL.createObjectURL(file);
|
||||||
|
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = file.name;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
document.body.removeChild(link);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
function resizePreviewFrame() {
|
||||||
|
const iframe = $('previewFrame');
|
||||||
|
const height = iframe.contentWindow.document.body.scrollHeight + 2;
|
||||||
|
iframe.style.setProperty('--expanded-height', height + 'px');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update status block colors based on selected status and error_source */
|
||||||
|
function updateStatusBlockStyles() {
|
||||||
|
const browserOk = $('browser_status').value === 'ok';
|
||||||
|
const cfOk = $('cloudflare_status').value === 'ok';
|
||||||
|
const hostOk = $('host_status').value === 'ok';
|
||||||
|
|
||||||
|
setBlockClass('block_browser', browserOk ? 'status-ok' : 'status-error');
|
||||||
|
setBlockClass('block_cloudflare', cfOk ? 'status-ok' : 'status-error');
|
||||||
|
setBlockClass('block_host', hostOk ? 'status-ok' : 'status-error');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setBlockClass(id, cls) {
|
||||||
|
const el = $(id);
|
||||||
|
if (!el) return;
|
||||||
|
el.classList.remove('status-ok', 'status-error');
|
||||||
|
el.classList.add(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple debounce */
|
||||||
|
function debounce(fn, wait) {
|
||||||
|
let t;
|
||||||
|
return (...args) => {
|
||||||
|
clearTimeout(t);
|
||||||
|
t = setTimeout(() => fn(...args), wait);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wire up events */
|
||||||
|
// initialize form values from initialConfig
|
||||||
|
loadConfig(initialConfig);
|
||||||
|
render();
|
||||||
|
|
||||||
|
// On preset change, load preset and render
|
||||||
|
$('presetSelect').addEventListener('change', (e) => {
|
||||||
|
const p = e.target.value;
|
||||||
|
if (PRESETS[p]) loadConfig(PRESETS[p]);
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Render / Open button handlers
|
||||||
|
// $('btnRender').addEventListener('click', e => { e.preventDefault(); render(); });
|
||||||
|
$('btnOpen').addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
openInNewTab();
|
||||||
|
});
|
||||||
|
$('btnShare').addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
createShareableLink();
|
||||||
|
});
|
||||||
|
$('btnExport').addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
exportJSON();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('btnCopyLink').addEventListener('click', () => {
|
||||||
|
const field = $('shareLink');
|
||||||
|
field.select();
|
||||||
|
field.setSelectionRange(0, field.value.length);
|
||||||
|
navigator.clipboard.writeText(field.value).then(() => {
|
||||||
|
// No notification required unless you want one
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Input change -> render
|
||||||
|
const inputs = document.querySelectorAll('#editorForm input, #editorForm textarea, #editorForm select');
|
||||||
|
inputs.forEach((inp) => {
|
||||||
|
inp.addEventListener(
|
||||||
|
'input',
|
||||||
|
debounce(() => {
|
||||||
|
// Update status block color classes for quick visual feedback in the editor
|
||||||
|
render();
|
||||||
|
}, 200)
|
||||||
|
);
|
||||||
|
// for radio change events (error_source)
|
||||||
|
if (inp.type === 'radio')
|
||||||
|
inp.addEventListener('change', () => {
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Automatically update frame height
|
||||||
|
const observer = new ResizeObserver((entries) => resizePreviewFrame());
|
||||||
|
const iframe = $('previewFrame');
|
||||||
|
observer.observe(iframe.contentWindow.document.body);
|
||||||
|
// resizePreviewFrame()
|
||||||
|
setInterval(resizePreviewFrame, 1000); // TODO...
|
||||||
45
editor/frontend/tsconfig.json
Normal file
45
editor/frontend/tsconfig.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
// Visit https://aka.ms/tsconfig to read more about this file
|
||||||
|
"include": ["src/**/*.ts"],
|
||||||
|
"compilerOptions": {
|
||||||
|
// File Layout
|
||||||
|
// "rootDir": "./src",
|
||||||
|
"outDir": "./dist",
|
||||||
|
|
||||||
|
// Environment Settings
|
||||||
|
// See also https://aka.ms/tsconfig/module
|
||||||
|
"module": "esnext",
|
||||||
|
"target": "esnext",
|
||||||
|
"types": [],
|
||||||
|
// For nodejs:
|
||||||
|
// "lib": ["esnext"],
|
||||||
|
// "types": ["node"],
|
||||||
|
// and npm install -D @types/node
|
||||||
|
|
||||||
|
// Other Outputs
|
||||||
|
"sourceMap": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
|
||||||
|
// Stricter Typechecking Options
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
|
||||||
|
// Style Options
|
||||||
|
// "noImplicitReturns": true,
|
||||||
|
// "noImplicitOverride": true,
|
||||||
|
// "noUnusedLocals": true,
|
||||||
|
// "noUnusedParameters": true,
|
||||||
|
// "noFallthroughCasesInSwitch": true,
|
||||||
|
// "noPropertyAccessFromIndexSignature": true,
|
||||||
|
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
// "verbatimModuleSyntax": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noUncheckedSideEffectImports": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"skipLibCheck": true
|
||||||
|
}
|
||||||
|
}
|
||||||
36
editor/frontend/vite.config.ts
Normal file
36
editor/frontend/vite.config.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import { minify as htmlMinify } from 'html-minifier-terser';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
appType: 'mpa',
|
||||||
|
base: '/editor/',
|
||||||
|
build: {
|
||||||
|
minify: true,
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
port: 3000,
|
||||||
|
proxy: {
|
||||||
|
'/s': {
|
||||||
|
target: 'http://localhost:5000',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: 'html-minifier',
|
||||||
|
transformIndexHtml: {
|
||||||
|
order: 'post',
|
||||||
|
handler(html) {
|
||||||
|
return htmlMinify(html, {
|
||||||
|
collapseWhitespace: true,
|
||||||
|
removeComments: true,
|
||||||
|
minifyCSS: true,
|
||||||
|
minifyJS: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
593
editor/frontend/yarn.lock
Normal file
593
editor/frontend/yarn.lock
Normal file
@@ -0,0 +1,593 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@esbuild/aix-ppc64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz#80fcbe36130e58b7670511e888b8e88a259ed76c"
|
||||||
|
integrity sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==
|
||||||
|
|
||||||
|
"@esbuild/android-arm64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz#8aa4965f8d0a7982dc21734bf6601323a66da752"
|
||||||
|
integrity sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==
|
||||||
|
|
||||||
|
"@esbuild/android-arm@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz#300712101f7f50f1d2627a162e6e09b109b6767a"
|
||||||
|
integrity sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==
|
||||||
|
|
||||||
|
"@esbuild/android-x64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz#87dfb27161202bdc958ef48bb61b09c758faee16"
|
||||||
|
integrity sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==
|
||||||
|
|
||||||
|
"@esbuild/darwin-arm64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz#79197898ec1ff745d21c071e1c7cc3c802f0c1fd"
|
||||||
|
integrity sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==
|
||||||
|
|
||||||
|
"@esbuild/darwin-x64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz#146400a8562133f45c4d2eadcf37ddd09718079e"
|
||||||
|
integrity sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==
|
||||||
|
|
||||||
|
"@esbuild/freebsd-arm64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz#1c5f9ba7206e158fd2b24c59fa2d2c8bb47ca0fe"
|
||||||
|
integrity sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==
|
||||||
|
|
||||||
|
"@esbuild/freebsd-x64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz#ea631f4a36beaac4b9279fa0fcc6ca29eaeeb2b3"
|
||||||
|
integrity sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==
|
||||||
|
|
||||||
|
"@esbuild/linux-arm64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz#e1066bce58394f1b1141deec8557a5f0a22f5977"
|
||||||
|
integrity sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==
|
||||||
|
|
||||||
|
"@esbuild/linux-arm@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz#452cd66b20932d08bdc53a8b61c0e30baf4348b9"
|
||||||
|
integrity sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==
|
||||||
|
|
||||||
|
"@esbuild/linux-ia32@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz#b24f8acc45bcf54192c7f2f3be1b53e6551eafe0"
|
||||||
|
integrity sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==
|
||||||
|
|
||||||
|
"@esbuild/linux-loong64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz#f9cfffa7fc8322571fbc4c8b3268caf15bd81ad0"
|
||||||
|
integrity sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==
|
||||||
|
|
||||||
|
"@esbuild/linux-mips64el@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz#575a14bd74644ffab891adc7d7e60d275296f2cd"
|
||||||
|
integrity sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==
|
||||||
|
|
||||||
|
"@esbuild/linux-ppc64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz#75b99c70a95fbd5f7739d7692befe60601591869"
|
||||||
|
integrity sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==
|
||||||
|
|
||||||
|
"@esbuild/linux-riscv64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz#2e3259440321a44e79ddf7535c325057da875cd6"
|
||||||
|
integrity sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==
|
||||||
|
|
||||||
|
"@esbuild/linux-s390x@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz#17676cabbfe5928da5b2a0d6df5d58cd08db2663"
|
||||||
|
integrity sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==
|
||||||
|
|
||||||
|
"@esbuild/linux-x64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz#0583775685ca82066d04c3507f09524d3cd7a306"
|
||||||
|
integrity sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==
|
||||||
|
|
||||||
|
"@esbuild/netbsd-arm64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz#f04c4049cb2e252fe96b16fed90f70746b13f4a4"
|
||||||
|
integrity sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==
|
||||||
|
|
||||||
|
"@esbuild/netbsd-x64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz#77da0d0a0d826d7c921eea3d40292548b258a076"
|
||||||
|
integrity sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==
|
||||||
|
|
||||||
|
"@esbuild/openbsd-arm64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz#6296f5867aedef28a81b22ab2009c786a952dccd"
|
||||||
|
integrity sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==
|
||||||
|
|
||||||
|
"@esbuild/openbsd-x64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz#f8d23303360e27b16cf065b23bbff43c14142679"
|
||||||
|
integrity sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==
|
||||||
|
|
||||||
|
"@esbuild/openharmony-arm64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz#49e0b768744a3924be0d7fd97dd6ce9b2923d88d"
|
||||||
|
integrity sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==
|
||||||
|
|
||||||
|
"@esbuild/sunos-x64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz#a6ed7d6778d67e528c81fb165b23f4911b9b13d6"
|
||||||
|
integrity sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==
|
||||||
|
|
||||||
|
"@esbuild/win32-arm64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz#9ac14c378e1b653af17d08e7d3ce34caef587323"
|
||||||
|
integrity sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==
|
||||||
|
|
||||||
|
"@esbuild/win32-ia32@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz#918942dcbbb35cc14fca39afb91b5e6a3d127267"
|
||||||
|
integrity sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==
|
||||||
|
|
||||||
|
"@esbuild/win32-x64@0.25.12":
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz#9bdad8176be7811ad148d1f8772359041f46c6c5"
|
||||||
|
integrity sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==
|
||||||
|
|
||||||
|
"@jridgewell/gen-mapping@^0.3.5":
|
||||||
|
version "0.3.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
|
||||||
|
integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.24"
|
||||||
|
|
||||||
|
"@jridgewell/resolve-uri@^3.1.0":
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6"
|
||||||
|
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
|
||||||
|
|
||||||
|
"@jridgewell/source-map@^0.3.3":
|
||||||
|
version "0.3.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.11.tgz#b21835cbd36db656b857c2ad02ebd413cc13a9ba"
|
||||||
|
integrity sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/gen-mapping" "^0.3.5"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.25"
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0":
|
||||||
|
version "1.5.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
|
||||||
|
integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
|
||||||
|
version "0.3.31"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
|
||||||
|
integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/resolve-uri" "^3.1.0"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||||
|
|
||||||
|
"@rollup/rollup-android-arm-eabi@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz#7e478b66180c5330429dd161bf84dad66b59c8eb"
|
||||||
|
integrity sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==
|
||||||
|
|
||||||
|
"@rollup/rollup-android-arm64@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz#2b025510c53a5e3962d3edade91fba9368c9d71c"
|
||||||
|
integrity sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==
|
||||||
|
|
||||||
|
"@rollup/rollup-darwin-arm64@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz#3577c38af68ccf34c03e84f476bfd526abca10a0"
|
||||||
|
integrity sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==
|
||||||
|
|
||||||
|
"@rollup/rollup-darwin-x64@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz#2bf5f2520a1f3b551723d274b9669ba5b75ed69c"
|
||||||
|
integrity sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==
|
||||||
|
|
||||||
|
"@rollup/rollup-freebsd-arm64@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz#4bb9cc80252564c158efc0710153c71633f1927c"
|
||||||
|
integrity sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==
|
||||||
|
|
||||||
|
"@rollup/rollup-freebsd-x64@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz#2301289094d49415a380cf942219ae9d8b127440"
|
||||||
|
integrity sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz#1d03d776f2065e09fc141df7d143476e94acca88"
|
||||||
|
integrity sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz#8623de0e040b2fd52a541c602688228f51f96701"
|
||||||
|
integrity sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm64-gnu@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz#ce2d1999bc166277935dde0301cde3dd0417fb6e"
|
||||||
|
integrity sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm64-musl@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz#88c2523778444da952651a2219026416564a4899"
|
||||||
|
integrity sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-loong64-gnu@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz#578ca2220a200ac4226c536c10c8cc6e4f276714"
|
||||||
|
integrity sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-ppc64-gnu@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz#aa338d3effd4168a20a5023834a74ba2c3081293"
|
||||||
|
integrity sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz#16ba582f9f6cff58119aa242782209b1557a1508"
|
||||||
|
integrity sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-riscv64-musl@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz#e404a77ebd6378483888b8064c703adb011340ab"
|
||||||
|
integrity sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-s390x-gnu@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz#92ad52d306227c56bec43d96ad2164495437ffe6"
|
||||||
|
integrity sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-x64-gnu@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz#fd0dea3bb9aa07e7083579f25e1c2285a46cb9fa"
|
||||||
|
integrity sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-x64-musl@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz#37a3efb09f18d555f8afc490e1f0444885de8951"
|
||||||
|
integrity sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==
|
||||||
|
|
||||||
|
"@rollup/rollup-openharmony-arm64@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz#c489bec9f4f8320d42c9b324cca220c90091c1f7"
|
||||||
|
integrity sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-arm64-msvc@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz#152832b5f79dc22d1606fac3db946283601b7080"
|
||||||
|
integrity sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-ia32-msvc@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz#54d91b2bb3bf3e9f30d32b72065a4e52b3a172a5"
|
||||||
|
integrity sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-gnu@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz#df9df03e61a003873efec8decd2034e7f135c71e"
|
||||||
|
integrity sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-msvc@4.53.3":
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz#38ae84f4c04226c1d56a3b17296ef1e0460ecdfe"
|
||||||
|
integrity sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==
|
||||||
|
|
||||||
|
"@types/estree@1.0.8":
|
||||||
|
version "1.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
|
||||||
|
integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
|
||||||
|
|
||||||
|
acorn@^8.15.0:
|
||||||
|
version "8.15.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
|
||||||
|
integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
|
||||||
|
|
||||||
|
async@^3.2.6:
|
||||||
|
version "3.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce"
|
||||||
|
integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==
|
||||||
|
|
||||||
|
balanced-match@^1.0.0:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||||
|
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||||
|
|
||||||
|
bootstrap@^5.3.8:
|
||||||
|
version "5.3.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-5.3.8.tgz#6401a10057a22752d21f4e19055508980656aeed"
|
||||||
|
integrity sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==
|
||||||
|
|
||||||
|
brace-expansion@^2.0.1:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7"
|
||||||
|
integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==
|
||||||
|
dependencies:
|
||||||
|
balanced-match "^1.0.0"
|
||||||
|
|
||||||
|
buffer-from@^1.0.0:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
||||||
|
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
|
||||||
|
|
||||||
|
camel-case@^4.1.2:
|
||||||
|
version "4.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
|
||||||
|
integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==
|
||||||
|
dependencies:
|
||||||
|
pascal-case "^3.1.2"
|
||||||
|
tslib "^2.0.3"
|
||||||
|
|
||||||
|
clean-css@~5.3.2:
|
||||||
|
version "5.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd"
|
||||||
|
integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==
|
||||||
|
dependencies:
|
||||||
|
source-map "~0.6.0"
|
||||||
|
|
||||||
|
commander@^10.0.0:
|
||||||
|
version "10.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
|
||||||
|
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
|
||||||
|
|
||||||
|
commander@^2.20.0:
|
||||||
|
version "2.20.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||||
|
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||||
|
|
||||||
|
dot-case@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
|
||||||
|
integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
|
||||||
|
dependencies:
|
||||||
|
no-case "^3.0.4"
|
||||||
|
tslib "^2.0.3"
|
||||||
|
|
||||||
|
ejs@^3.1.10:
|
||||||
|
version "3.1.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b"
|
||||||
|
integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==
|
||||||
|
dependencies:
|
||||||
|
jake "^10.8.5"
|
||||||
|
|
||||||
|
entities@^4.4.0:
|
||||||
|
version "4.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||||
|
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||||
|
|
||||||
|
esbuild@^0.25.0:
|
||||||
|
version "0.25.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.12.tgz#97a1d041f4ab00c2fce2f838d2b9969a2d2a97a5"
|
||||||
|
integrity sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==
|
||||||
|
optionalDependencies:
|
||||||
|
"@esbuild/aix-ppc64" "0.25.12"
|
||||||
|
"@esbuild/android-arm" "0.25.12"
|
||||||
|
"@esbuild/android-arm64" "0.25.12"
|
||||||
|
"@esbuild/android-x64" "0.25.12"
|
||||||
|
"@esbuild/darwin-arm64" "0.25.12"
|
||||||
|
"@esbuild/darwin-x64" "0.25.12"
|
||||||
|
"@esbuild/freebsd-arm64" "0.25.12"
|
||||||
|
"@esbuild/freebsd-x64" "0.25.12"
|
||||||
|
"@esbuild/linux-arm" "0.25.12"
|
||||||
|
"@esbuild/linux-arm64" "0.25.12"
|
||||||
|
"@esbuild/linux-ia32" "0.25.12"
|
||||||
|
"@esbuild/linux-loong64" "0.25.12"
|
||||||
|
"@esbuild/linux-mips64el" "0.25.12"
|
||||||
|
"@esbuild/linux-ppc64" "0.25.12"
|
||||||
|
"@esbuild/linux-riscv64" "0.25.12"
|
||||||
|
"@esbuild/linux-s390x" "0.25.12"
|
||||||
|
"@esbuild/linux-x64" "0.25.12"
|
||||||
|
"@esbuild/netbsd-arm64" "0.25.12"
|
||||||
|
"@esbuild/netbsd-x64" "0.25.12"
|
||||||
|
"@esbuild/openbsd-arm64" "0.25.12"
|
||||||
|
"@esbuild/openbsd-x64" "0.25.12"
|
||||||
|
"@esbuild/openharmony-arm64" "0.25.12"
|
||||||
|
"@esbuild/sunos-x64" "0.25.12"
|
||||||
|
"@esbuild/win32-arm64" "0.25.12"
|
||||||
|
"@esbuild/win32-ia32" "0.25.12"
|
||||||
|
"@esbuild/win32-x64" "0.25.12"
|
||||||
|
|
||||||
|
fdir@^6.5.0:
|
||||||
|
version "6.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350"
|
||||||
|
integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
|
||||||
|
|
||||||
|
filelist@^1.0.4:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5"
|
||||||
|
integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==
|
||||||
|
dependencies:
|
||||||
|
minimatch "^5.0.1"
|
||||||
|
|
||||||
|
fsevents@~2.3.2, fsevents@~2.3.3:
|
||||||
|
version "2.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||||
|
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||||
|
|
||||||
|
html-minifier-terser@^7.2.0:
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz#18752e23a2f0ed4b0f550f217bb41693e975b942"
|
||||||
|
integrity sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==
|
||||||
|
dependencies:
|
||||||
|
camel-case "^4.1.2"
|
||||||
|
clean-css "~5.3.2"
|
||||||
|
commander "^10.0.0"
|
||||||
|
entities "^4.4.0"
|
||||||
|
param-case "^3.0.4"
|
||||||
|
relateurl "^0.2.7"
|
||||||
|
terser "^5.15.1"
|
||||||
|
|
||||||
|
jake@^10.8.5:
|
||||||
|
version "10.9.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.4.tgz#d626da108c63d5cfb00ab5c25fadc7e0084af8e6"
|
||||||
|
integrity sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==
|
||||||
|
dependencies:
|
||||||
|
async "^3.2.6"
|
||||||
|
filelist "^1.0.4"
|
||||||
|
picocolors "^1.1.1"
|
||||||
|
|
||||||
|
lower-case@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
|
||||||
|
integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.0.3"
|
||||||
|
|
||||||
|
minimatch@^5.0.1:
|
||||||
|
version "5.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
|
||||||
|
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^2.0.1"
|
||||||
|
|
||||||
|
nanoid@^3.3.11:
|
||||||
|
version "3.3.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
|
||||||
|
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
|
||||||
|
|
||||||
|
no-case@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
|
||||||
|
integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
|
||||||
|
dependencies:
|
||||||
|
lower-case "^2.0.2"
|
||||||
|
tslib "^2.0.3"
|
||||||
|
|
||||||
|
param-case@^3.0.4:
|
||||||
|
version "3.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
|
||||||
|
integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==
|
||||||
|
dependencies:
|
||||||
|
dot-case "^3.0.4"
|
||||||
|
tslib "^2.0.3"
|
||||||
|
|
||||||
|
pascal-case@^3.1.2:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
|
||||||
|
integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==
|
||||||
|
dependencies:
|
||||||
|
no-case "^3.0.4"
|
||||||
|
tslib "^2.0.3"
|
||||||
|
|
||||||
|
picocolors@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||||
|
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||||
|
|
||||||
|
picomatch@^4.0.3:
|
||||||
|
version "4.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042"
|
||||||
|
integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
|
||||||
|
|
||||||
|
postcss@^8.5.6:
|
||||||
|
version "8.5.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
|
||||||
|
integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
|
||||||
|
dependencies:
|
||||||
|
nanoid "^3.3.11"
|
||||||
|
picocolors "^1.1.1"
|
||||||
|
source-map-js "^1.2.1"
|
||||||
|
|
||||||
|
prettier@3.7.4:
|
||||||
|
version "3.7.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.7.4.tgz#d2f8335d4b1cec47e1c8098645411b0c9dff9c0f"
|
||||||
|
integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==
|
||||||
|
|
||||||
|
relateurl@^0.2.7:
|
||||||
|
version "0.2.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
|
||||||
|
integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==
|
||||||
|
|
||||||
|
rollup@^4.43.0:
|
||||||
|
version "4.53.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.53.3.tgz#dbc8cd8743b38710019fb8297e8d7a76e3faa406"
|
||||||
|
integrity sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==
|
||||||
|
dependencies:
|
||||||
|
"@types/estree" "1.0.8"
|
||||||
|
optionalDependencies:
|
||||||
|
"@rollup/rollup-android-arm-eabi" "4.53.3"
|
||||||
|
"@rollup/rollup-android-arm64" "4.53.3"
|
||||||
|
"@rollup/rollup-darwin-arm64" "4.53.3"
|
||||||
|
"@rollup/rollup-darwin-x64" "4.53.3"
|
||||||
|
"@rollup/rollup-freebsd-arm64" "4.53.3"
|
||||||
|
"@rollup/rollup-freebsd-x64" "4.53.3"
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf" "4.53.3"
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf" "4.53.3"
|
||||||
|
"@rollup/rollup-linux-arm64-gnu" "4.53.3"
|
||||||
|
"@rollup/rollup-linux-arm64-musl" "4.53.3"
|
||||||
|
"@rollup/rollup-linux-loong64-gnu" "4.53.3"
|
||||||
|
"@rollup/rollup-linux-ppc64-gnu" "4.53.3"
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu" "4.53.3"
|
||||||
|
"@rollup/rollup-linux-riscv64-musl" "4.53.3"
|
||||||
|
"@rollup/rollup-linux-s390x-gnu" "4.53.3"
|
||||||
|
"@rollup/rollup-linux-x64-gnu" "4.53.3"
|
||||||
|
"@rollup/rollup-linux-x64-musl" "4.53.3"
|
||||||
|
"@rollup/rollup-openharmony-arm64" "4.53.3"
|
||||||
|
"@rollup/rollup-win32-arm64-msvc" "4.53.3"
|
||||||
|
"@rollup/rollup-win32-ia32-msvc" "4.53.3"
|
||||||
|
"@rollup/rollup-win32-x64-gnu" "4.53.3"
|
||||||
|
"@rollup/rollup-win32-x64-msvc" "4.53.3"
|
||||||
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
source-map-js@^1.2.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||||
|
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||||
|
|
||||||
|
source-map-support@~0.5.20:
|
||||||
|
version "0.5.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
|
||||||
|
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
|
||||||
|
dependencies:
|
||||||
|
buffer-from "^1.0.0"
|
||||||
|
source-map "^0.6.0"
|
||||||
|
|
||||||
|
source-map@^0.6.0, source-map@~0.6.0:
|
||||||
|
version "0.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||||
|
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||||
|
|
||||||
|
terser@^5.15.1:
|
||||||
|
version "5.44.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/terser/-/terser-5.44.1.tgz#e391e92175c299b8c284ad6ded609e37303b0a9c"
|
||||||
|
integrity sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/source-map" "^0.3.3"
|
||||||
|
acorn "^8.15.0"
|
||||||
|
commander "^2.20.0"
|
||||||
|
source-map-support "~0.5.20"
|
||||||
|
|
||||||
|
tinyglobby@^0.2.15:
|
||||||
|
version "0.2.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2"
|
||||||
|
integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==
|
||||||
|
dependencies:
|
||||||
|
fdir "^6.5.0"
|
||||||
|
picomatch "^4.0.3"
|
||||||
|
|
||||||
|
tslib@^2.0.3:
|
||||||
|
version "2.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
||||||
|
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||||
|
|
||||||
|
typescript@^5.9.3:
|
||||||
|
version "5.9.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f"
|
||||||
|
integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
|
||||||
|
|
||||||
|
vite@^7.2.6:
|
||||||
|
version "7.2.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/vite/-/vite-7.2.7.tgz#0789a4c3206081699f34a9ecca2dda594a07478e"
|
||||||
|
integrity sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==
|
||||||
|
dependencies:
|
||||||
|
esbuild "^0.25.0"
|
||||||
|
fdir "^6.5.0"
|
||||||
|
picomatch "^4.0.3"
|
||||||
|
postcss "^8.5.6"
|
||||||
|
rollup "^4.43.0"
|
||||||
|
tinyglobby "^0.2.15"
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents "~2.3.3"
|
||||||
@@ -1,866 +0,0 @@
|
|||||||
<!-- SPDX-License-Identifier: MIT -->
|
|
||||||
<!--
|
|
||||||
!!!
|
|
||||||
!!! Note: This file is vibely generated, and could be very hard to maintain.
|
|
||||||
!!!
|
|
||||||
-->
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>Cloudflare Error Page Editor</title>
|
|
||||||
|
|
||||||
<meta name="description" content="Online editor to create customized Cloudflare-styled error pages.">
|
|
||||||
<meta name="keywords" content="cloudflare,error,page,editor">
|
|
||||||
<link rel="canonical" href="https://virt.moe/cloudflare-error-page/editor/" />
|
|
||||||
|
|
||||||
<meta property="og:type" content="website" />
|
|
||||||
<meta property="og:site_name" content="moe::virt" />
|
|
||||||
<meta property="og:title" content="Cloudflare error page editor" />
|
|
||||||
<meta property="og:url" content="https://virt.moe/cloudflare-error-page/editor/" />
|
|
||||||
<meta property="og:description" content="Online editor to create customized Cloudflare-styled error pages" />
|
|
||||||
|
|
||||||
<meta property="twitter:card" content="summary" />
|
|
||||||
<meta property="twitter:site" content="moe::virt" />
|
|
||||||
<meta property="twitter:title" content="Cloudflare error page editor" />
|
|
||||||
<meta property="twitter:description" content="Online editor to create customized Cloudflare-styled error pages" />
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
||||||
|
|
||||||
<script src="https://virt.moe/assets/cloudflare-error-page/ejs.min.js"></script>
|
|
||||||
<link href="https://virt.moe/assets/cloudflare-error-page/bootstrap.min.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<script>
|
|
||||||
(async function () {
|
|
||||||
/*
|
|
||||||
Editor logic
|
|
||||||
- initialConfig: provided default config (from your JSON)
|
|
||||||
- render(): placeholder that generates HTML and writes to iframe.srcdoc
|
|
||||||
- inputs call render() on change
|
|
||||||
- "Open in new tab" opens the rendered HTML in a new window using a blob URL
|
|
||||||
*/
|
|
||||||
let template;
|
|
||||||
let postponeRendering = false;
|
|
||||||
|
|
||||||
fetch('template.ejs')
|
|
||||||
.then(response => {
|
|
||||||
if (!response.ok) {
|
|
||||||
alert('failed to fetch template');
|
|
||||||
}
|
|
||||||
return response.text(); // Returns a Promise
|
|
||||||
})
|
|
||||||
.then(templateContent => {
|
|
||||||
template = ejs.compile(templateContent)
|
|
||||||
if (postponeRendering) {
|
|
||||||
render()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// can be changed if specified by '?from=<name>'
|
|
||||||
let initialConfig = {
|
|
||||||
"title": "Internal server error",
|
|
||||||
"error_code": '500',
|
|
||||||
"more_information": {
|
|
||||||
"hidden": false,
|
|
||||||
"text": "cloudflare.com",
|
|
||||||
"for": "more information",
|
|
||||||
},
|
|
||||||
"browser_status": {
|
|
||||||
"status": "ok",
|
|
||||||
"location": "You",
|
|
||||||
"name": "Browser",
|
|
||||||
"status_text": "Working"
|
|
||||||
},
|
|
||||||
"cloudflare_status": {
|
|
||||||
"status": "error",
|
|
||||||
"location": "San Francisco",
|
|
||||||
"name": "Cloudflare",
|
|
||||||
"status_text": "Error"
|
|
||||||
},
|
|
||||||
"host_status": {
|
|
||||||
"status": "ok",
|
|
||||||
"location": "Website",
|
|
||||||
"name": "Host",
|
|
||||||
"status_text": "Working"
|
|
||||||
},
|
|
||||||
"error_source": "cloudflare",
|
|
||||||
"what_happened": "There is an internal server error on Cloudflare's network.",
|
|
||||||
"what_can_i_do": "Please try again in a few minutes.",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Demo presets (content brief — replace or expand as needed)
|
|
||||||
const PRESETS = {
|
|
||||||
"default": structuredClone(initialConfig),
|
|
||||||
"empty": {
|
|
||||||
"error_code": '500',
|
|
||||||
},
|
|
||||||
"catastrophic": {
|
|
||||||
"title": "Catastrophic infrastructure failure",
|
|
||||||
"error_code": '500',
|
|
||||||
"more_information": {
|
|
||||||
"for": "no information",
|
|
||||||
},
|
|
||||||
"browser_status": {
|
|
||||||
"status": "error",
|
|
||||||
"status_text": "Out of Memory"
|
|
||||||
},
|
|
||||||
"cloudflare_status": {
|
|
||||||
"status": "error",
|
|
||||||
"location": "Everywhere",
|
|
||||||
"status_text": "Error"
|
|
||||||
},
|
|
||||||
"host_status": {
|
|
||||||
"status": "error",
|
|
||||||
"location": "example.com",
|
|
||||||
"status_text": "On Fire"
|
|
||||||
},
|
|
||||||
"error_source": "cloudflare",
|
|
||||||
"what_happened": "There is a catastrophic failure.",
|
|
||||||
"what_can_i_do": "Please try again in a few years.",
|
|
||||||
},
|
|
||||||
"working": {
|
|
||||||
"title": "Web server is working",
|
|
||||||
"error_code": '200',
|
|
||||||
"more_information": {
|
|
||||||
"hidden": true
|
|
||||||
},
|
|
||||||
"browser_status": {
|
|
||||||
"status": "ok",
|
|
||||||
"status_text": "Seems Working"
|
|
||||||
},
|
|
||||||
"cloudflare_status": {
|
|
||||||
"status": "ok",
|
|
||||||
"status_text": "Often Working"
|
|
||||||
},
|
|
||||||
"host_status": {
|
|
||||||
"status": "ok",
|
|
||||||
"location": "example.com",
|
|
||||||
"status_text": "Almost Working"
|
|
||||||
},
|
|
||||||
"error_source": "host",
|
|
||||||
"what_happened": "This site is still working. And it looks great.",
|
|
||||||
"what_can_i_do": "Visit the site before it crashes someday."
|
|
||||||
},
|
|
||||||
"consensual": {
|
|
||||||
"title": "The Myth Of \"Consensual\" Internet",
|
|
||||||
"error_code": "lmao",
|
|
||||||
"more_information": {
|
|
||||||
"hidden": false,
|
|
||||||
"text": "r/ProgrammerHumor",
|
|
||||||
"link": "https://redd.it/1p2yola"
|
|
||||||
},
|
|
||||||
"browser_status": {
|
|
||||||
"status": "ok",
|
|
||||||
"location": "You",
|
|
||||||
"name": "Browser",
|
|
||||||
"status_text": "I Consent"
|
|
||||||
},
|
|
||||||
"cloudflare_status": {
|
|
||||||
"status": "error",
|
|
||||||
"location": "F***ing Everywhere",
|
|
||||||
"name": "Cloudflare",
|
|
||||||
"status_text": "I Don't!"
|
|
||||||
},
|
|
||||||
"host_status": {
|
|
||||||
"status": "ok",
|
|
||||||
"location": "Remote",
|
|
||||||
"name": "Host",
|
|
||||||
"status_text": "I Consent"
|
|
||||||
},
|
|
||||||
"error_source": "cloudflare",
|
|
||||||
"what_happened": "Isn't There Someone You Forgot To Ask?",
|
|
||||||
"what_can_i_do": "Kill Yourself",
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function extractUrlParam(str, key) {
|
|
||||||
const urlParams = new URLSearchParams(str)
|
|
||||||
return urlParams.get(key)
|
|
||||||
}
|
|
||||||
function getDefaultPresetName() {
|
|
||||||
const key = 'from';
|
|
||||||
let name = extractUrlParam(window.location.search, key);
|
|
||||||
if (!name) {
|
|
||||||
name = extractUrlParam(window.location.hash.substr(1), key)
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
const defaultPresetName = getDefaultPresetName();
|
|
||||||
if (defaultPresetName && defaultPresetName.indexOf('/') < 0) {
|
|
||||||
fetch(`../s/${defaultPresetName}`, {
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json'
|
|
||||||
},
|
|
||||||
}).then(response => {
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('failed to get preset');
|
|
||||||
}
|
|
||||||
return response.json();
|
|
||||||
}).then(result => {
|
|
||||||
if (result.status != 'ok') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
console.log(result.parameters)
|
|
||||||
initialConfig = result.parameters
|
|
||||||
loadConfig(initialConfig);
|
|
||||||
render()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Utilities */
|
|
||||||
function $(id) { return document.getElementById(id); }
|
|
||||||
|
|
||||||
/* Fill form from config */
|
|
||||||
function loadConfig(cfg) {
|
|
||||||
$('title').value = cfg.title ?? '';
|
|
||||||
$('error_code').value = cfg.error_code ?? '';
|
|
||||||
|
|
||||||
$('more_hidden').checked = !!(cfg.more_information && cfg.more_information.hidden);
|
|
||||||
$('more_text').value = cfg.more_information?.text ?? '';
|
|
||||||
$('more_link').value = cfg.more_information?.link ?? '';
|
|
||||||
$('more_for').value = cfg.more_information?.for ?? '';
|
|
||||||
|
|
||||||
$('browser_status').value = cfg.browser_status?.status ?? 'ok';
|
|
||||||
$('browser_location').value = cfg.browser_status?.location ?? '';
|
|
||||||
$('browser_name').value = cfg.browser_status?.name ?? '';
|
|
||||||
$('browser_status_text').value = cfg.browser_status?.status_text ?? '';
|
|
||||||
|
|
||||||
$('cloudflare_status').value = cfg.cloudflare_status?.status ?? 'ok';
|
|
||||||
$('cloudflare_location').value = cfg.cloudflare_status?.location ?? '';
|
|
||||||
$('cloudflare_name').value = cfg.cloudflare_status?.name ?? '';
|
|
||||||
$('cloudflare_status_text').value = cfg.cloudflare_status?.status_text ?? '';
|
|
||||||
|
|
||||||
$('host_status').value = cfg.host_status?.status ?? 'ok';
|
|
||||||
$('host_location').value = cfg.host_status?.location ?? '';
|
|
||||||
$('host_name').value = cfg.host_status?.name ?? '';
|
|
||||||
$('host_status_text').value = cfg.host_status?.status_text ?? '';
|
|
||||||
|
|
||||||
if (cfg.error_source === 'browser') $('err_browser').checked = true;
|
|
||||||
else if (cfg.error_source === 'cloudflare') $('err_cloudflare').checked = true;
|
|
||||||
else $('err_host').checked = true;
|
|
||||||
|
|
||||||
$('what_happened').value = cfg.what_happened ?? '';
|
|
||||||
$('what_can_i_do').value = cfg.what_can_i_do ?? '';
|
|
||||||
|
|
||||||
$('perf_text').value = cfg.perf_sec_by?.text ?? '';
|
|
||||||
$('perf_link').value = cfg.perf_sec_by?.link ?? '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read config from form inputs */
|
|
||||||
function readConfig() {
|
|
||||||
return {
|
|
||||||
title: $('title').value,
|
|
||||||
error_code: $('error_code').value,
|
|
||||||
more_information: {
|
|
||||||
hidden: !!$('more_hidden').checked,
|
|
||||||
text: $('more_text').value,
|
|
||||||
link: $('more_link').value,
|
|
||||||
for: $('more_for').value
|
|
||||||
},
|
|
||||||
browser_status: {
|
|
||||||
status: $('browser_status').value,
|
|
||||||
location: $('browser_location').value,
|
|
||||||
name: $('browser_name').value,
|
|
||||||
status_text: $('browser_status_text').value
|
|
||||||
},
|
|
||||||
cloudflare_status: {
|
|
||||||
status: $('cloudflare_status').value,
|
|
||||||
location: $('cloudflare_location').value,
|
|
||||||
name: $('cloudflare_name').value,
|
|
||||||
status_text: $('cloudflare_status_text').value
|
|
||||||
},
|
|
||||||
host_status: {
|
|
||||||
status: $('host_status').value,
|
|
||||||
location: $('host_location').value,
|
|
||||||
name: $('host_name').value,
|
|
||||||
status_text: $('host_status_text').value
|
|
||||||
},
|
|
||||||
error_source: (document.querySelector('input[name="error_source"]:checked') || { value: 'host' }).value,
|
|
||||||
what_happened: $('what_happened').value,
|
|
||||||
what_can_i_do: $('what_can_i_do').value,
|
|
||||||
perf_sec_by: {
|
|
||||||
text: $('perf_text').value,
|
|
||||||
link: $('perf_link').value
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function formatUtcTimestamp() {
|
|
||||||
const d = new Date();
|
|
||||||
|
|
||||||
const year = d.getUTCFullYear();
|
|
||||||
const month = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
||||||
const day = String(d.getUTCDate()).padStart(2, "0");
|
|
||||||
|
|
||||||
const hours = String(d.getUTCHours()).padStart(2, "0");
|
|
||||||
const minutes = String(d.getUTCMinutes()).padStart(2, "0");
|
|
||||||
const seconds = String(d.getUTCSeconds()).padStart(2, "0");
|
|
||||||
|
|
||||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} UTC`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderEjs(params) {
|
|
||||||
return template({
|
|
||||||
params: params,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Basic render: build HTML string from config and put into iframe.srcdoc */
|
|
||||||
function render() {
|
|
||||||
const cfg = readConfig();
|
|
||||||
window.lastCfg = cfg
|
|
||||||
|
|
||||||
if (!template) {
|
|
||||||
postponeRendering = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.time = formatUtcTimestamp()
|
|
||||||
cfg.ray_id = '0123456789abcdef'
|
|
||||||
cfg.client_ip = '1.1.1.1'
|
|
||||||
if (Number.isNaN(Number(cfg.error_code))) {
|
|
||||||
cfg.html_title = cfg.title || 'Internal server error'
|
|
||||||
}
|
|
||||||
|
|
||||||
let pageHtml = renderEjs(cfg)
|
|
||||||
// Write into iframe
|
|
||||||
const iframe = $('previewFrame');
|
|
||||||
let doc = iframe.contentDocument
|
|
||||||
doc.open();
|
|
||||||
doc.write(pageHtml);
|
|
||||||
doc.close();
|
|
||||||
|
|
||||||
updateStatusBlockStyles();
|
|
||||||
|
|
||||||
// store last rendered HTML for "open in new tab"
|
|
||||||
lastRenderedHtml = pageHtml;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open in new tab: create blob and open */
|
|
||||||
let lastRenderedHtml = '';
|
|
||||||
function openInNewTab() {
|
|
||||||
if (!lastRenderedHtml) render();
|
|
||||||
const blob = new Blob([lastRenderedHtml], { type: 'text/html' });
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
window.open(url, '_blank', 'noopener');
|
|
||||||
// note that this url won't be revoked
|
|
||||||
}
|
|
||||||
|
|
||||||
function createShareableLink() {
|
|
||||||
$('shareLink').value = ''
|
|
||||||
fetch('../s/create', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
'parameters': window.lastCfg
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
if (!response.ok) {
|
|
||||||
alert('failed to create link');
|
|
||||||
}
|
|
||||||
return response.json();
|
|
||||||
})
|
|
||||||
.then(result => {
|
|
||||||
if (result.status != 'ok') {
|
|
||||||
alert('failed to create link');
|
|
||||||
return
|
|
||||||
}
|
|
||||||
$('shareLink').value = result.url
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function exportJSON() {
|
|
||||||
let content = JSON.stringify(lastCfg, null, 4)
|
|
||||||
const file = new File([content], 'cloudflare-error-page-params.json', {
|
|
||||||
type: 'text/plain',
|
|
||||||
})
|
|
||||||
const url = URL.createObjectURL(file)
|
|
||||||
|
|
||||||
const link = document.createElement('a')
|
|
||||||
link.href = url
|
|
||||||
link.download = file.name
|
|
||||||
document.body.appendChild(link)
|
|
||||||
link.click()
|
|
||||||
|
|
||||||
document.body.removeChild(link)
|
|
||||||
window.URL.revokeObjectURL(url)
|
|
||||||
}
|
|
||||||
function resizePreviewFrame() {
|
|
||||||
const iframe = $('previewFrame');
|
|
||||||
const height = iframe.contentWindow.document.body.scrollHeight + 2
|
|
||||||
iframe.style.setProperty('--expanded-height', height + 'px')
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wire up events */
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
// initialize form values from initialConfig
|
|
||||||
loadConfig(initialConfig);
|
|
||||||
render();
|
|
||||||
|
|
||||||
// On preset change, load preset and render
|
|
||||||
$('presetSelect').addEventListener('change', e => {
|
|
||||||
const p = e.target.value;
|
|
||||||
if (PRESETS[p]) loadConfig(PRESETS[p]);
|
|
||||||
render();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Render / Open button handlers
|
|
||||||
// $('btnRender').addEventListener('click', e => { e.preventDefault(); render(); });
|
|
||||||
$('btnOpen').addEventListener('click', e => { e.preventDefault(); openInNewTab(); });
|
|
||||||
$('btnShare').addEventListener('click', e => { e.preventDefault(); createShareableLink() });
|
|
||||||
$('btnExport').addEventListener('click', e => { e.preventDefault(); exportJSON() });
|
|
||||||
|
|
||||||
$('btnCopyLink').addEventListener('click', () => {
|
|
||||||
const field = $('shareLink');
|
|
||||||
field.select();
|
|
||||||
field.setSelectionRange(0, field.value.length);
|
|
||||||
navigator.clipboard.writeText(field.value).then(() => {
|
|
||||||
// No notification required unless you want one
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Input change -> render
|
|
||||||
const inputs = document.querySelectorAll('#editorForm input, #editorForm textarea, #editorForm select');
|
|
||||||
inputs.forEach(inp => {
|
|
||||||
inp.addEventListener('input', debounce(() => {
|
|
||||||
// Update status block color classes for quick visual feedback in the editor
|
|
||||||
render();
|
|
||||||
}, 200));
|
|
||||||
// for radio change events (error_source)
|
|
||||||
if (inp.type === 'radio') inp.addEventListener('change', () => { render(); });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Automatically update frame height
|
|
||||||
const observer = new ResizeObserver(entries => resizePreviewFrame());
|
|
||||||
const iframe = $('previewFrame');
|
|
||||||
observer.observe(iframe.contentWindow.document.body);
|
|
||||||
// resizePreviewFrame()
|
|
||||||
setInterval(resizePreviewFrame, 1000) // TODO...
|
|
||||||
});
|
|
||||||
|
|
||||||
/* Update status block colors based on selected status and error_source */
|
|
||||||
function updateStatusBlockStyles() {
|
|
||||||
const browserOk = $('browser_status').value === 'ok';
|
|
||||||
const cfOk = $('cloudflare_status').value === 'ok';
|
|
||||||
const hostOk = $('host_status').value === 'ok';
|
|
||||||
|
|
||||||
setBlockClass('block_browser', browserOk ? 'status-ok' : 'status-error');
|
|
||||||
setBlockClass('block_cloudflare', cfOk ? 'status-ok' : 'status-error');
|
|
||||||
setBlockClass('block_host', hostOk ? 'status-ok' : 'status-error');
|
|
||||||
}
|
|
||||||
|
|
||||||
function setBlockClass(id, cls) {
|
|
||||||
const el = $(id);
|
|
||||||
if (!el) return;
|
|
||||||
el.classList.remove('status-ok', 'status-error');
|
|
||||||
el.classList.add(cls);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Simple debounce */
|
|
||||||
function debounce(fn, wait) {
|
|
||||||
let t;
|
|
||||||
return (...args) => {
|
|
||||||
clearTimeout(t);
|
|
||||||
t = setTimeout(() => fn(...args), wait);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
</script>
|
|
||||||
<style>
|
|
||||||
/* Layout: editor + preview */
|
|
||||||
.app {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
height: 100vh;
|
|
||||||
/* padding: 0.8rem; */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* On md and up, arrange horizontally: editor left, preview right */
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.app {
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor {
|
|
||||||
flex: 0 0;
|
|
||||||
min-width: 380px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview {
|
|
||||||
flex: 1 1 48%;
|
|
||||||
/* max-width: 48%; */
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
iframe.preview-frame {
|
|
||||||
height: 100% !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* On small screens use stacked layout: editor then iframe */
|
|
||||||
.editor {
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #e3e6ea;
|
|
||||||
border-radius: .5rem;
|
|
||||||
padding: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview {
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #e3e6ea;
|
|
||||||
border-radius: .5rem;
|
|
||||||
padding: 0.8rem 0 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compact form: label and control same row */
|
|
||||||
.form-row {
|
|
||||||
display: flex;
|
|
||||||
gap: .3rem;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: .6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-row>label {
|
|
||||||
min-width: 6rem;
|
|
||||||
max-width: 12rem;
|
|
||||||
margin-bottom: 0;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-row>.control {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Status block styling */
|
|
||||||
.status-block {
|
|
||||||
border: 1px solid #cfeadd;
|
|
||||||
border-radius: .375rem;
|
|
||||||
padding: .75rem;
|
|
||||||
margin-bottom: .5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-ok {
|
|
||||||
background: #e9f7ef;
|
|
||||||
border: 1px solid #cfeadd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-error {
|
|
||||||
background: #fff5f5;
|
|
||||||
border: 1px solid #f3c2c2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Iframe styling */
|
|
||||||
iframe.preview-frame {
|
|
||||||
--expanded-height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
height: var(--expanded-height);
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
min-height: 360px;
|
|
||||||
border-radius: .375rem;
|
|
||||||
box-shadow: 0 1px 4px rgba(0, 0, 0, .06);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Controls toolbar */
|
|
||||||
.toolbar {
|
|
||||||
display: flex;
|
|
||||||
gap: .5rem;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: .75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compact textarea resizing */
|
|
||||||
textarea.compact {
|
|
||||||
min-height: 80px;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="bg-light">
|
|
||||||
|
|
||||||
<div class="container-fluid h-100">
|
|
||||||
<div class="app">
|
|
||||||
|
|
||||||
<!-- Editor column -->
|
|
||||||
<div class="editor">
|
|
||||||
<h5 class="form-row">Cloudflare Error Page Editor</h5>
|
|
||||||
<hr>
|
|
||||||
<div class="form-row mb-3">
|
|
||||||
<label for="presetSelect">Preset</label>
|
|
||||||
<select id="presetSelect" class="form-select form-select-sm">
|
|
||||||
<option value="default">Internal server error (Default)</option>
|
|
||||||
<option value="empty">Empty</option>
|
|
||||||
<option value="catastrophic">Catastrophic failure</option>
|
|
||||||
<option value="working">Server working</option>
|
|
||||||
<option value="consensual">Myth of consensual</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<form id="editorForm" class="needs-validation" novalidate>
|
|
||||||
<!-- Basic properties -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<!-- <h6 class="mb-2">Page</h6> -->
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="title">Title</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="title" class="form-control form-control-sm" placeholder="Internal server error" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="error_code">Error Code</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="error_code" class="form-control form-control-sm" placeholder="500" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<!-- Status blocks -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<h6 class="mb-2">Status</h6>
|
|
||||||
|
|
||||||
<!-- Browser -->
|
|
||||||
<div id="block_browser" class="status-block status-ok">
|
|
||||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
||||||
<strong>Browser</strong>
|
|
||||||
<div>
|
|
||||||
<input class="form-check-input" type="radio" name="error_source" id="err_browser" value="browser" />
|
|
||||||
<label for="err_browser" class="ms-1 small">Error here</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="browser_status">Status</label>
|
|
||||||
<div class="control">
|
|
||||||
<select id="browser_status" class="form-select form-select-sm">
|
|
||||||
<option value="ok">Ok</option>
|
|
||||||
<option value="error">Error</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="browser_location">Location</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="browser_location" class="form-control form-control-sm" placeholder="You" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="browser_name">Name</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="browser_name" class="form-control form-control-sm" placeholder="Browser" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="browser_status_text">Status Text</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="browser_status_text" class="form-control form-control-sm" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Cloudflare -->
|
|
||||||
<div id="block_cloudflare" class="status-block status-error">
|
|
||||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
||||||
<strong>Cloudflare</strong>
|
|
||||||
<div>
|
|
||||||
<input class="form-check-input" type="radio" name="error_source" id="err_cloudflare"
|
|
||||||
value="cloudflare" />
|
|
||||||
<label for="err_cloudflare" class="ms-1 small">Error here</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="cloudflare_status">Status</label>
|
|
||||||
<div class="control">
|
|
||||||
<select id="cloudflare_status" class="form-select form-select-sm">
|
|
||||||
<option value="ok">Ok</option>
|
|
||||||
<option value="error">Error</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="cloudflare_location">Location</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="cloudflare_location" class="form-control form-control-sm" placeholder="San Francisco" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="cloudflare_name">Name</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="cloudflare_name" class="form-control form-control-sm" placeholder="Cloudflare" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="cloudflare_status_text">Status Text</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="cloudflare_status_text" class="form-control form-control-sm" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Host -->
|
|
||||||
<div id="block_host" class="status-block status-ok">
|
|
||||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
||||||
<strong>Host</strong>
|
|
||||||
<div>
|
|
||||||
<input class="form-check-input" type="radio" name="error_source" id="err_host" value="host" />
|
|
||||||
<label for="err_host" class="ms-1 small">Error here</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="host_status">Status</label>
|
|
||||||
<div class="control">
|
|
||||||
<select id="host_status" class="form-select form-select-sm">
|
|
||||||
<option value="ok">Ok</option>
|
|
||||||
<option value="error">Error</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="host_location">Location</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="host_location" class="form-control form-control-sm" placeholder="Website" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="host_name">Name</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="host_name" class="form-control form-control-sm" placeholder="Host" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="host_status_text">Status Text</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="host_status_text" class="form-control form-control-sm" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="status-block mt-3 mb-3">
|
|
||||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
||||||
<strong>Visit ...</strong>
|
|
||||||
<div>
|
|
||||||
<input id="more_hidden" class="form-check-input" type="checkbox" />
|
|
||||||
<label for="more_hidden" class="ms-1 small">Hidden</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="more_text">Text</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="more_text" class="form-control form-control-sm" placeholder="cloudflare.com" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="more_link">Link</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="more_link" class="form-control form-control-sm" placeholder="https://www.cloudflare.com/" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="more_for">For</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="more_for" class="form-control form-control-sm" placeholder="more information" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label for="what_happened" class="fw-semibold">What happened?</label>
|
|
||||||
<div class="control">
|
|
||||||
<textarea id="what_happened" class="form-control compact"
|
|
||||||
placeholder="There is an internal server error on Cloudflare's network."></textarea>
|
|
||||||
</div>
|
|
||||||
<!-- </div> -->
|
|
||||||
|
|
||||||
<!-- <div class=""> -->
|
|
||||||
<label for="what_can_i_do" class="fw-semibold mt-2">What can I do?</label>
|
|
||||||
<div class="control">
|
|
||||||
<textarea id="what_can_i_do" class="form-control compact"
|
|
||||||
placeholder="Please try again in a few minutes."></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<h6 class="form-row">Performance & security by ...</h6>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="perf_text">Text</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="perf_text" class="form-control form-control-sm" placeholder="Cloudflare" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-row">
|
|
||||||
<label for="perf_link">Link</label>
|
|
||||||
<div class="control">
|
|
||||||
<input id="perf_link" class="form-control form-control-sm" placeholder="https://www.cloudflare.com/" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- </div> -->
|
|
||||||
|
|
||||||
<div class="d-flex gap-2 mt-2 mb-2">
|
|
||||||
<!-- <button id="btnRender" class="btn btn-sm btn-primary">Render</button> -->
|
|
||||||
<button id="btnOpen" class="btn btn-sm btn-primary">Preview in new tab</button>
|
|
||||||
<button id="btnExport" class="btn btn-sm btn-primary">Export JSON</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button id="btnShare" class="btn btn-sm btn-primary">Create shareable link</button>
|
|
||||||
<div class="mt-2">
|
|
||||||
<div class="input-group input-group-sm">
|
|
||||||
<input id="shareLink" class="form-control" readonly />
|
|
||||||
<button id="btnCopyLink" class="btn btn-outline-secondary" type="button">Copy</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-2 text-center">>> Star this project on
|
|
||||||
<a href="https://github.com/donlon/cloudflare-error-page" target="_blank">GitHub</a> ⭐
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-2" style="font-size: 0.9em;">You can also embed this error page into your own website. See
|
|
||||||
<a href="https://github.com/donlon/cloudflare-error-page#quickstart-for-programmers"
|
|
||||||
target="_blank">Quickstart</a> in the
|
|
||||||
homepage for steps.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Preview column -->
|
|
||||||
<div class="preview">
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-1" style="padding: 0 0.8em;">
|
|
||||||
<h6><strong>Preview</strong></h6>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- TODO: An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing. -->
|
|
||||||
<iframe id="previewFrame" class="preview-frame" sandbox="allow-scripts allow-same-origin"></iframe>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -8,7 +8,7 @@ from flask import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
root_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../')
|
root_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../')
|
||||||
res_folder = os.path.join(root_dir, 'editor/resources')
|
res_folder = os.path.join(root_dir, 'editor/frontend/dist')
|
||||||
|
|
||||||
bp = Blueprint('editor', __name__, url_prefix='/')
|
bp = Blueprint('editor', __name__, url_prefix='/')
|
||||||
|
|
||||||
|
|||||||
@@ -69,5 +69,5 @@ if __name__ == '__main__':
|
|||||||
inline_css_resource(
|
inline_css_resource(
|
||||||
os.path.join(resources_folder, 'templates/error.ejs'),
|
os.path.join(resources_folder, 'templates/error.ejs'),
|
||||||
os.path.join(resources_folder, 'styles/main.css'),
|
os.path.join(resources_folder, 'styles/main.css'),
|
||||||
os.path.join(root, 'editor/resources/template.ejs'),
|
os.path.join(root, 'editor/frontend/src/template.ejs'),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user