ui: add lightweight frontend build pipeline
This commit is contained in:
@@ -122,6 +122,7 @@ Crank - платформа для публикации внешних API в в
|
||||
```bash
|
||||
cd apps/ui
|
||||
npm ci
|
||||
npm run build
|
||||
npm run e2e:install
|
||||
npm run e2e
|
||||
```
|
||||
|
||||
@@ -2,20 +2,18 @@
|
||||
|
||||
## Current
|
||||
|
||||
### `feat/frontend-build-pipeline`
|
||||
### `feat/frontend-css-state-cleanup`
|
||||
|
||||
Status: in_progress
|
||||
|
||||
DoD:
|
||||
- UI assets are bundled through a lightweight pipeline suitable for the current stack
|
||||
- local source files remain readable, but shipped JS is no longer served as dozens of unbundled scripts
|
||||
- vendor dependencies move under package-managed installation where practical
|
||||
- Docker UI image builds the frontend assets before packaging them into nginx
|
||||
- existing UI e2e coverage stays green after the build change
|
||||
- button and control state visuals move from inline `element.style` mutations into CSS classes
|
||||
- wizard navigation and similar interactive UI use semantic state classes instead of manual style strings
|
||||
- existing UI e2e coverage stays green after the CSS-state cleanup
|
||||
|
||||
## Next
|
||||
|
||||
- `feat/frontend-css-state-cleanup`
|
||||
- `feat/frontend-performance-polish`
|
||||
|
||||
## Backlog
|
||||
|
||||
@@ -27,7 +25,6 @@ DoD:
|
||||
- `feat/frontend-template-safety-cleanup`
|
||||
- `feat/frontend-shell-unification`
|
||||
- `feat/frontend-wizard-modularization`
|
||||
- `feat/frontend-build-pipeline`
|
||||
- `feat/frontend-css-state-cleanup`
|
||||
- `feat/frontend-performance-polish`
|
||||
- `feat/frontend-observability-and-testability`
|
||||
|
||||
+13
-7
@@ -1,12 +1,18 @@
|
||||
FROM node:22-alpine AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY apps/ui/package.json apps/ui/package-lock.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY apps/ui ./
|
||||
COPY Crank.png ./Crank.png
|
||||
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:1.27-alpine
|
||||
|
||||
COPY apps/ui/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY apps/ui/index.html /usr/share/nginx/html/index.html
|
||||
COPY apps/ui/css /usr/share/nginx/html/css
|
||||
COPY apps/ui/data /usr/share/nginx/html/data
|
||||
COPY apps/ui/html /usr/share/nginx/html/html
|
||||
COPY apps/ui/icons /usr/share/nginx/html/icons
|
||||
COPY apps/ui/js /usr/share/nginx/html/js
|
||||
COPY Crank.png /usr/share/nginx/html/Crank.png
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
@@ -11,14 +11,7 @@
|
||||
<link rel="stylesheet" href="css/pages.css">
|
||||
<link rel="stylesheet" href="css/catalog.css">
|
||||
<link rel="stylesheet" href="css/wizard.css">
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/dom.js"></script>
|
||||
<script src="js/workspace.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/ui-feedback.js"></script>
|
||||
<script src="js/auth.js"></script>
|
||||
<script>window.CrankAuth.guardProtectedPage();</script>
|
||||
<script src="%CRANK_BUNDLE_PROTECTED_CORE%"></script>
|
||||
</head>
|
||||
<body x-data="agents">
|
||||
|
||||
@@ -393,7 +386,6 @@
|
||||
</div><!-- /drawer -->
|
||||
|
||||
|
||||
<script src="js/vendor/alpine/cdn.min.js" defer></script>
|
||||
<script src="js/agents.js"></script>
|
||||
<script src="%CRANK_BUNDLE_AGENTS%"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -23,14 +23,7 @@
|
||||
.scope-checkbox-name { font-size: 13px; font-weight: 500; color: var(--text-primary); }
|
||||
.scope-checkbox-desc { font-size: 11.5px; color: var(--text-muted); }
|
||||
</style>
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/dom.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/ui-feedback.js"></script>
|
||||
<script src="js/auth.js"></script>
|
||||
<script>window.CrankAuth.guardProtectedPage();</script>
|
||||
<script src="js/workspace.js"></script>
|
||||
<script src="%CRANK_BUNDLE_PROTECTED_CORE%"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -277,8 +270,7 @@
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script src="js/nav.js"></script>
|
||||
<script src="js/api-keys.js"></script>
|
||||
<script src="%CRANK_BUNDLE_API_KEYS%"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,14 +9,7 @@
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/layout.css">
|
||||
<link rel="stylesheet" href="css/pages.css">
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/dom.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/ui-feedback.js"></script>
|
||||
<script src="js/workspace.js"></script>
|
||||
<script src="js/auth.js"></script>
|
||||
<script>window.CrankAuth.guardProtectedPage();</script>
|
||||
<script src="%CRANK_BUNDLE_PROTECTED_CORE%"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar">
|
||||
@@ -108,7 +101,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/nav.js"></script>
|
||||
<script src="js/async-jobs.js"></script>
|
||||
<script src="%CRANK_BUNDLE_ASYNC_JOBS%"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -8,10 +8,7 @@
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/login.css">
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/auth.js"></script>
|
||||
<script src="%CRANK_BUNDLE_LOGIN%"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -52,7 +49,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/login.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+2
-10
@@ -10,14 +10,7 @@
|
||||
<link rel="stylesheet" href="css/layout.css">
|
||||
<link rel="stylesheet" href="css/pages.css">
|
||||
<link rel="stylesheet" href="css/logs.css">
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/dom.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/ui-feedback.js"></script>
|
||||
<script src="js/workspace.js"></script>
|
||||
<script src="js/auth.js"></script>
|
||||
<script>window.CrankAuth.guardProtectedPage();</script>
|
||||
<script src="%CRANK_BUNDLE_PROTECTED_CORE%"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -143,8 +136,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<script src="js/nav.js"></script>
|
||||
<script src="js/logs.js"></script>
|
||||
<script src="%CRANK_BUNDLE_LOGS%"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -40,14 +40,7 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/dom.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/ui-feedback.js"></script>
|
||||
<script src="js/auth.js"></script>
|
||||
<script>window.CrankAuth.guardProtectedPage();</script>
|
||||
<script src="js/workspace.js"></script>
|
||||
<script src="%CRANK_BUNDLE_PROTECTED_CORE%"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar">
|
||||
@@ -236,7 +229,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/nav.js"></script>
|
||||
<script src="js/secrets.js"></script>
|
||||
<script src="%CRANK_BUNDLE_SECRETS%"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -10,14 +10,7 @@
|
||||
<link rel="stylesheet" href="css/layout.css">
|
||||
<link rel="stylesheet" href="css/pages.css">
|
||||
<link rel="stylesheet" href="css/settings.css">
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/dom.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/ui-feedback.js"></script>
|
||||
<script src="js/workspace.js"></script>
|
||||
<script src="js/auth.js"></script>
|
||||
<script>window.CrankAuth.guardProtectedPage();</script>
|
||||
<script src="%CRANK_BUNDLE_PROTECTED_CORE%"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -327,8 +320,7 @@
|
||||
|
||||
</div><!-- /page -->
|
||||
|
||||
<script src="js/nav.js"></script>
|
||||
<script src="js/settings.js"></script>
|
||||
<script src="%CRANK_BUNDLE_SETTINGS%"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,14 +9,7 @@
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/layout.css">
|
||||
<link rel="stylesheet" href="css/pages.css">
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/dom.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/ui-feedback.js"></script>
|
||||
<script src="js/workspace.js"></script>
|
||||
<script src="js/auth.js"></script>
|
||||
<script>window.CrankAuth.guardProtectedPage();</script>
|
||||
<script src="%CRANK_BUNDLE_PROTECTED_CORE%"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar">
|
||||
@@ -113,7 +106,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/nav.js"></script>
|
||||
<script src="js/stream-sessions.js"></script>
|
||||
<script src="%CRANK_BUNDLE_STREAM_SESSIONS%"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+2
-10
@@ -10,14 +10,7 @@
|
||||
<link rel="stylesheet" href="css/layout.css">
|
||||
<link rel="stylesheet" href="css/pages.css">
|
||||
<link rel="stylesheet" href="css/usage.css">
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/dom.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/ui-feedback.js"></script>
|
||||
<script src="js/workspace.js"></script>
|
||||
<script src="js/auth.js"></script>
|
||||
<script>window.CrankAuth.guardProtectedPage();</script>
|
||||
<script src="%CRANK_BUNDLE_PROTECTED_CORE%"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -225,8 +218,7 @@
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script src="js/nav.js"></script>
|
||||
<script src="js/usage.js"></script>
|
||||
<script src="%CRANK_BUNDLE_USAGE%"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -9,17 +9,7 @@
|
||||
<link rel="stylesheet" href="../../css/layout.css">
|
||||
<link rel="stylesheet" href="../../css/wizard.css">
|
||||
<link rel="stylesheet" href="../../css/pages.css">
|
||||
<script src="../../js/config.js"></script>
|
||||
<script src="../../js/i18n.js"></script>
|
||||
<script src="../../js/dom.js"></script>
|
||||
<script src="../../js/wizard-state.js"></script>
|
||||
<script src="../../js/wizard-shell.js"></script>
|
||||
<script src="../../js/workspace.js"></script>
|
||||
<script src="../../js/api.js"></script>
|
||||
<script src="../../js/ui-feedback.js"></script>
|
||||
<script src="../../js/auth.js"></script>
|
||||
<script>window.CrankAuth.guardProtectedPage();</script>
|
||||
<script src="../../js/vendor/js-yaml/js-yaml.min.js"></script>
|
||||
<script src="%CRANK_BUNDLE_PROTECTED_CORE%"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -236,8 +226,6 @@
|
||||
|
||||
</div><!-- /wizard-shell -->
|
||||
|
||||
<script src="../../js/streaming-form.js"></script>
|
||||
<script src="../../js/stream-test-run.js"></script>
|
||||
<div class="modal-overlay" id="quick-secret-modal">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
@@ -273,8 +261,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../../js/wizard.js"></script>
|
||||
<script src="../../js/nav.js"></script>
|
||||
<script src="%CRANK_BUNDLE_WIZARD%"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -11,13 +11,7 @@
|
||||
<link rel="stylesheet" href="css/pages.css">
|
||||
<link rel="stylesheet" href="css/wizard.css">
|
||||
<link rel="stylesheet" href="css/workspace-setup.css">
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/dom.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/ui-feedback.js"></script>
|
||||
<script src="js/auth.js"></script>
|
||||
<script>window.CrankAuth.guardProtectedPage();</script>
|
||||
<script src="%CRANK_BUNDLE_PROTECTED_CORE%"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="ws-setup-page">
|
||||
@@ -327,7 +321,6 @@
|
||||
|
||||
</div>
|
||||
|
||||
<script src="js/workspace.js"></script>
|
||||
<script src="js/workspace-setup.js"></script>
|
||||
<script src="%CRANK_BUNDLE_WORKSPACE_SETUP%"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+2
-10
@@ -9,14 +9,7 @@
|
||||
<link rel="stylesheet" href="css/layout.css">
|
||||
<link rel="stylesheet" href="css/catalog.css">
|
||||
<link rel="stylesheet" href="css/pages.css">
|
||||
<script src="js/config.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/dom.js"></script>
|
||||
<script src="js/workspace.js"></script>
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/ui-feedback.js"></script>
|
||||
<script src="js/auth.js"></script>
|
||||
<script>window.CrankAuth.guardProtectedPage();</script>
|
||||
<script src="%CRANK_BUNDLE_PROTECTED_CORE%"></script>
|
||||
</head>
|
||||
<body x-data="catalog()">
|
||||
|
||||
@@ -419,8 +412,7 @@
|
||||
|
||||
</div><!-- /page -->
|
||||
|
||||
<script src="js/catalog.js" defer></script>
|
||||
<script src="js/vendor/alpine/cdn.min.js" defer></script>
|
||||
<script src="%CRANK_BUNDLE_OPERATIONS%"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+27
-45
@@ -3,7 +3,7 @@
|
||||
return typeof t === 'function' ? t(key) : key;
|
||||
}
|
||||
|
||||
var state = {
|
||||
var defaults = {
|
||||
currentStep: 1,
|
||||
wizardProtocol: 'rest',
|
||||
wizardMode: 'create',
|
||||
@@ -27,6 +27,22 @@
|
||||
selectedRpcMethod: null,
|
||||
};
|
||||
|
||||
Object.keys(defaults).forEach(function(key) {
|
||||
if (typeof window[key] !== 'undefined') {
|
||||
return;
|
||||
}
|
||||
var value = defaults[key];
|
||||
if (Array.isArray(value)) {
|
||||
window[key] = value.slice();
|
||||
return;
|
||||
}
|
||||
if (value && typeof value === 'object') {
|
||||
window[key] = Object.assign({}, value);
|
||||
return;
|
||||
}
|
||||
window[key] = value;
|
||||
});
|
||||
|
||||
function defaultProtocolCapabilities() {
|
||||
return {
|
||||
rest: {
|
||||
@@ -53,28 +69,28 @@
|
||||
}
|
||||
|
||||
function currentProtocolCapabilities() {
|
||||
var capabilities = state.wizardProtocolCapabilities || defaultProtocolCapabilities();
|
||||
return capabilities[state.wizardProtocol] || capabilities.rest;
|
||||
var capabilities = window.wizardProtocolCapabilities || defaultProtocolCapabilities();
|
||||
return capabilities[window.wizardProtocol] || capabilities.rest;
|
||||
}
|
||||
|
||||
async function loadProtocolCapabilities() {
|
||||
if (!state.wizardWorkspaceId || !window.CrankApi || typeof window.CrankApi.getProtocolCapabilities !== 'function') {
|
||||
state.wizardProtocolCapabilities = defaultProtocolCapabilities();
|
||||
return state.wizardProtocolCapabilities;
|
||||
if (!window.wizardWorkspaceId || !window.CrankApi || typeof window.CrankApi.getProtocolCapabilities !== 'function') {
|
||||
window.wizardProtocolCapabilities = defaultProtocolCapabilities();
|
||||
return window.wizardProtocolCapabilities;
|
||||
}
|
||||
|
||||
try {
|
||||
var response = await window.CrankApi.getProtocolCapabilities(state.wizardWorkspaceId);
|
||||
var response = await window.CrankApi.getProtocolCapabilities(window.wizardWorkspaceId);
|
||||
var mapped = {};
|
||||
(response.items || []).forEach(function(item) {
|
||||
mapped[item.protocol] = item;
|
||||
});
|
||||
state.wizardProtocolCapabilities = mapped;
|
||||
window.wizardProtocolCapabilities = mapped;
|
||||
} catch (_error) {
|
||||
state.wizardProtocolCapabilities = defaultProtocolCapabilities();
|
||||
window.wizardProtocolCapabilities = defaultProtocolCapabilities();
|
||||
}
|
||||
|
||||
return state.wizardProtocolCapabilities;
|
||||
return window.wizardProtocolCapabilities;
|
||||
}
|
||||
|
||||
function step3Labels() {
|
||||
@@ -88,44 +104,10 @@
|
||||
}
|
||||
|
||||
window.CrankWizardState = {
|
||||
state: state,
|
||||
state: window,
|
||||
defaultProtocolCapabilities: defaultProtocolCapabilities,
|
||||
currentProtocolCapabilities: currentProtocolCapabilities,
|
||||
loadProtocolCapabilities: loadProtocolCapabilities,
|
||||
step3Labels: step3Labels,
|
||||
};
|
||||
|
||||
[
|
||||
'currentStep',
|
||||
'wizardProtocol',
|
||||
'wizardMode',
|
||||
'wizardEditId',
|
||||
'wizardWorkspaceId',
|
||||
'wizardCurrentOperation',
|
||||
'wizardCurrentVersion',
|
||||
'wizardProtoUpload',
|
||||
'wizardDescriptorSetUpload',
|
||||
'wizardSoapWsdlUpload',
|
||||
'wizardSoapXsdUpload',
|
||||
'wizardTestResponsePreview',
|
||||
'grpcDescriptorServices',
|
||||
'soapServiceCatalog',
|
||||
'wizardProtocolCapabilities',
|
||||
'wizardSecrets',
|
||||
'wizardAuthProfiles',
|
||||
'selectedUpstreamId',
|
||||
'editingUpstreamId',
|
||||
'protoParsed',
|
||||
'selectedRpcMethod'
|
||||
].forEach(function(key) {
|
||||
Object.defineProperty(window, key, {
|
||||
configurable: true,
|
||||
get: function() {
|
||||
return state[key];
|
||||
},
|
||||
set: function(value) {
|
||||
state[key] = value;
|
||||
},
|
||||
});
|
||||
});
|
||||
}());
|
||||
|
||||
+21
-7
@@ -13,6 +13,27 @@ var _doGoToStep = window.CrankWizardShell.doGoToStep;
|
||||
var loadWizardPanels = window.CrankWizardShell.loadWizardPanels;
|
||||
var bindProtocolCards = window.CrankWizardShell.bindProtocolCards;
|
||||
var step3PanelId = window.CrankWizardShell.step3PanelId;
|
||||
var currentStep = window.currentStep;
|
||||
var wizardProtocol = window.wizardProtocol;
|
||||
var wizardMode = window.wizardMode;
|
||||
var wizardEditId = window.wizardEditId;
|
||||
var wizardWorkspaceId = window.wizardWorkspaceId;
|
||||
var wizardCurrentOperation = window.wizardCurrentOperation;
|
||||
var wizardCurrentVersion = window.wizardCurrentVersion;
|
||||
var wizardProtoUpload = window.wizardProtoUpload;
|
||||
var wizardDescriptorSetUpload = window.wizardDescriptorSetUpload;
|
||||
var wizardSoapWsdlUpload = window.wizardSoapWsdlUpload;
|
||||
var wizardSoapXsdUpload = window.wizardSoapXsdUpload;
|
||||
var wizardTestResponsePreview = window.wizardTestResponsePreview;
|
||||
var grpcDescriptorServices = window.grpcDescriptorServices;
|
||||
var soapServiceCatalog = window.soapServiceCatalog;
|
||||
var wizardProtocolCapabilities = window.wizardProtocolCapabilities;
|
||||
var wizardSecrets = window.wizardSecrets;
|
||||
var wizardAuthProfiles = window.wizardAuthProfiles;
|
||||
var selectedUpstreamId = window.selectedUpstreamId;
|
||||
var editingUpstreamId = window.editingUpstreamId;
|
||||
var protoParsed = window.protoParsed;
|
||||
var selectedRpcMethod = window.selectedRpcMethod;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async function() {
|
||||
document.querySelector('.btn-continue').addEventListener('click', function() {
|
||||
@@ -163,11 +184,7 @@ var upstreams = [
|
||||
{ id: 'grpcb', name: 'grpcb.in', url: 'https://grpcb.in:443', staticHeaders: '{}', authProfileId: null, authProfileName: '', authKind: null }
|
||||
];
|
||||
|
||||
var wizardSecrets = [];
|
||||
var wizardAuthProfiles = [];
|
||||
var selectedUpstreamId = null;
|
||||
var dropdownOpen = false;
|
||||
var editingUpstreamId = null;
|
||||
|
||||
function authProfileById(authProfileId) {
|
||||
return wizardAuthProfiles.find(function(item) { return item.id === authProfileId; }) || null;
|
||||
@@ -738,9 +755,6 @@ function selectGrpcSource(source) {
|
||||
gRPC — Proto file parser & UI
|
||||
══════════════════════════════════════════════ */
|
||||
|
||||
var protoParsed = null; // { package, services, messages, streamingCount }
|
||||
var selectedRpcMethod = null; // { service, method, request, response }
|
||||
|
||||
/* Extract top-level blocks with proper brace matching */
|
||||
function extractBlocks(text, keyword) {
|
||||
var blocks = [];
|
||||
|
||||
Generated
+486
-1
@@ -10,7 +10,450 @@
|
||||
"js-yaml": "4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.59.1"
|
||||
"@playwright/test": "^1.59.1",
|
||||
"esbuild": "^0.28.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
|
||||
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz",
|
||||
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz",
|
||||
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz",
|
||||
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz",
|
||||
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz",
|
||||
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz",
|
||||
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz",
|
||||
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz",
|
||||
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openharmony-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz",
|
||||
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz",
|
||||
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz",
|
||||
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
@@ -59,6 +502,48 @@
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.28.0",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz",
|
||||
"integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.28.0",
|
||||
"@esbuild/android-arm": "0.28.0",
|
||||
"@esbuild/android-arm64": "0.28.0",
|
||||
"@esbuild/android-x64": "0.28.0",
|
||||
"@esbuild/darwin-arm64": "0.28.0",
|
||||
"@esbuild/darwin-x64": "0.28.0",
|
||||
"@esbuild/freebsd-arm64": "0.28.0",
|
||||
"@esbuild/freebsd-x64": "0.28.0",
|
||||
"@esbuild/linux-arm": "0.28.0",
|
||||
"@esbuild/linux-arm64": "0.28.0",
|
||||
"@esbuild/linux-ia32": "0.28.0",
|
||||
"@esbuild/linux-loong64": "0.28.0",
|
||||
"@esbuild/linux-mips64el": "0.28.0",
|
||||
"@esbuild/linux-ppc64": "0.28.0",
|
||||
"@esbuild/linux-riscv64": "0.28.0",
|
||||
"@esbuild/linux-s390x": "0.28.0",
|
||||
"@esbuild/linux-x64": "0.28.0",
|
||||
"@esbuild/netbsd-arm64": "0.28.0",
|
||||
"@esbuild/netbsd-x64": "0.28.0",
|
||||
"@esbuild/openbsd-arm64": "0.28.0",
|
||||
"@esbuild/openbsd-x64": "0.28.0",
|
||||
"@esbuild/openharmony-arm64": "0.28.0",
|
||||
"@esbuild/sunos-x64": "0.28.0",
|
||||
"@esbuild/win32-arm64": "0.28.0",
|
||||
"@esbuild/win32-ia32": "0.28.0",
|
||||
"@esbuild/win32-x64": "0.28.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "crank-ui",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"sync:vendor": "mkdir -p js/vendor/alpine js/vendor/js-yaml && cp node_modules/alpinejs/dist/cdn.min.js js/vendor/alpine/cdn.min.js && cp node_modules/js-yaml/dist/js-yaml.min.js js/vendor/js-yaml/js-yaml.min.js",
|
||||
"build": "node scripts/build.js",
|
||||
"e2e:install": "playwright install chromium",
|
||||
"e2e": "playwright test",
|
||||
"e2e:headed": "playwright test --headed"
|
||||
@@ -12,6 +12,7 @@
|
||||
"js-yaml": "4.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.59.1"
|
||||
"@playwright/test": "^1.59.1",
|
||||
"esbuild": "^0.28.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
const { transform } = require('esbuild');
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT_DIR = path.resolve(__dirname, '..');
|
||||
const DIST_DIR = path.join(ROOT_DIR, 'dist');
|
||||
const BUNDLE_DIR = path.join(DIST_DIR, 'js', 'bundles');
|
||||
const BRAND_IMAGE_PATHS = [
|
||||
path.join(ROOT_DIR, 'Crank.png'),
|
||||
path.resolve(ROOT_DIR, '..', '..', 'Crank.png'),
|
||||
];
|
||||
|
||||
const BUNDLES = {
|
||||
'protected-core': {
|
||||
files: [
|
||||
'js/config.js',
|
||||
'js/i18n.js',
|
||||
'js/dom.js',
|
||||
'js/workspace.js',
|
||||
'js/api.js',
|
||||
'js/ui-feedback.js',
|
||||
'js/auth.js',
|
||||
],
|
||||
footer: 'window.CrankAuth.guardProtectedPage();\n',
|
||||
},
|
||||
login: {
|
||||
files: [
|
||||
'js/config.js',
|
||||
'js/i18n.js',
|
||||
'js/api.js',
|
||||
'js/auth.js',
|
||||
'js/login.js',
|
||||
],
|
||||
},
|
||||
operations: {
|
||||
files: [
|
||||
'node_modules/alpinejs/dist/cdn.min.js',
|
||||
'js/catalog.js',
|
||||
],
|
||||
},
|
||||
agents: {
|
||||
files: [
|
||||
'node_modules/alpinejs/dist/cdn.min.js',
|
||||
'js/agents.js',
|
||||
],
|
||||
},
|
||||
'api-keys': {
|
||||
files: [
|
||||
'js/nav.js',
|
||||
'js/api-keys.js',
|
||||
],
|
||||
},
|
||||
logs: {
|
||||
files: [
|
||||
'js/nav.js',
|
||||
'js/logs.js',
|
||||
],
|
||||
},
|
||||
usage: {
|
||||
files: [
|
||||
'js/nav.js',
|
||||
'js/usage.js',
|
||||
],
|
||||
},
|
||||
secrets: {
|
||||
files: [
|
||||
'js/nav.js',
|
||||
'js/secrets.js',
|
||||
],
|
||||
},
|
||||
settings: {
|
||||
files: [
|
||||
'js/nav.js',
|
||||
'js/settings.js',
|
||||
],
|
||||
},
|
||||
'stream-sessions': {
|
||||
files: [
|
||||
'js/nav.js',
|
||||
'js/stream-sessions.js',
|
||||
],
|
||||
},
|
||||
'async-jobs': {
|
||||
files: [
|
||||
'js/nav.js',
|
||||
'js/async-jobs.js',
|
||||
],
|
||||
},
|
||||
'workspace-setup': {
|
||||
files: [
|
||||
'js/workspace-setup.js',
|
||||
],
|
||||
},
|
||||
wizard: {
|
||||
files: [
|
||||
'node_modules/js-yaml/dist/js-yaml.min.js',
|
||||
'js/wizard-state.js',
|
||||
'js/wizard-shell.js',
|
||||
'js/streaming-form.js',
|
||||
'js/stream-test-run.js',
|
||||
'js/wizard.js',
|
||||
'js/nav.js',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
function sha(input) {
|
||||
return crypto.createHash('sha256').update(input).digest('hex').slice(0, 12);
|
||||
}
|
||||
|
||||
function sourcePath(relativePath) {
|
||||
return path.join(ROOT_DIR, relativePath);
|
||||
}
|
||||
|
||||
function ensureDirectory(directoryPath) {
|
||||
fs.mkdirSync(directoryPath, { recursive: true });
|
||||
}
|
||||
|
||||
function removeDirectory(directoryPath) {
|
||||
fs.rmSync(directoryPath, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function copyFile(source, destination) {
|
||||
ensureDirectory(path.dirname(destination));
|
||||
fs.copyFileSync(source, destination);
|
||||
}
|
||||
|
||||
function copyDirectory(source, destination) {
|
||||
ensureDirectory(destination);
|
||||
for (const entry of fs.readdirSync(source, { withFileTypes: true })) {
|
||||
const sourceEntry = path.join(source, entry.name);
|
||||
const destinationEntry = path.join(destination, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
copyDirectory(sourceEntry, destinationEntry);
|
||||
continue;
|
||||
}
|
||||
copyFile(sourceEntry, destinationEntry);
|
||||
}
|
||||
}
|
||||
|
||||
function readSource(relativePath) {
|
||||
return fs.readFileSync(sourcePath(relativePath), 'utf8');
|
||||
}
|
||||
|
||||
function resolveBrandImage() {
|
||||
const match = BRAND_IMAGE_PATHS.find(function(filePath) {
|
||||
return fs.existsSync(filePath);
|
||||
});
|
||||
if (!match) {
|
||||
throw new Error('unable to resolve Crank.png for UI build');
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
async function buildBundle(name, config) {
|
||||
const source = config.files.map(readSource).join('\n\n') + '\n' + (config.footer || '');
|
||||
const result = await transform(source, {
|
||||
loader: 'js',
|
||||
minify: true,
|
||||
target: 'es2019',
|
||||
legalComments: 'none',
|
||||
});
|
||||
const hash = sha(result.code);
|
||||
const fileName = `${name}.${hash}.js`;
|
||||
const relativeOutputPath = path.posix.join('js', 'bundles', fileName);
|
||||
fs.writeFileSync(path.join(BUNDLE_DIR, fileName), result.code);
|
||||
return relativeOutputPath;
|
||||
}
|
||||
|
||||
function rewriteHtml(relativePath, replacements, prefix) {
|
||||
const source = readSource(relativePath);
|
||||
const output = source.replace(/%CRANK_BUNDLE_([A-Z0-9_]+)%/g, function(_match, token) {
|
||||
const key = token.toLowerCase().replace(/_/g, '-');
|
||||
const filePath = replacements[key];
|
||||
if (!filePath) {
|
||||
throw new Error(`missing bundle placeholder ${key}`);
|
||||
}
|
||||
return prefix ? path.posix.join(prefix, filePath) : filePath;
|
||||
});
|
||||
const destination = path.join(DIST_DIR, relativePath);
|
||||
ensureDirectory(path.dirname(destination));
|
||||
fs.writeFileSync(destination, output);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
removeDirectory(DIST_DIR);
|
||||
ensureDirectory(BUNDLE_DIR);
|
||||
|
||||
const manifest = {};
|
||||
for (const [name, config] of Object.entries(BUNDLES)) {
|
||||
manifest[name] = await buildBundle(name, config);
|
||||
}
|
||||
|
||||
copyDirectory(path.join(ROOT_DIR, 'css'), path.join(DIST_DIR, 'css'));
|
||||
copyDirectory(path.join(ROOT_DIR, 'data'), path.join(DIST_DIR, 'data'));
|
||||
copyDirectory(path.join(ROOT_DIR, 'html', 'wizard'), path.join(DIST_DIR, 'html', 'wizard'));
|
||||
copyDirectory(path.join(ROOT_DIR, 'icons'), path.join(DIST_DIR, 'icons'));
|
||||
copyFile(resolveBrandImage(), path.join(DIST_DIR, 'Crank.png'));
|
||||
|
||||
rewriteHtml('index.html', manifest, '');
|
||||
rewriteHtml('html/login.html', manifest, '..');
|
||||
rewriteHtml('html/agents.html', manifest, '..');
|
||||
rewriteHtml('html/api-keys.html', manifest, '..');
|
||||
rewriteHtml('html/async-jobs.html', manifest, '..');
|
||||
rewriteHtml('html/logs.html', manifest, '..');
|
||||
rewriteHtml('html/secrets.html', manifest, '..');
|
||||
rewriteHtml('html/settings.html', manifest, '..');
|
||||
rewriteHtml('html/stream-sessions.html', manifest, '..');
|
||||
rewriteHtml('html/usage.html', manifest, '..');
|
||||
rewriteHtml('html/workspace-setup.html', manifest, '..');
|
||||
rewriteHtml('html/wizard/index.html', manifest, '../..');
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(DIST_DIR, 'build-manifest.json'),
|
||||
JSON.stringify(manifest, null, 2) + '\n',
|
||||
);
|
||||
}
|
||||
|
||||
main().catch(function(error) {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
@@ -19,6 +19,11 @@ ADMIN_PASSWORD="${CRANK_E2E_ADMIN_PASSWORD:-change-me-admin-password}"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
(
|
||||
cd "$ROOT_DIR/apps/ui"
|
||||
npm run build >"$LOG_DIR/ui-build.log" 2>&1
|
||||
)
|
||||
|
||||
kill_port_processes() {
|
||||
local port="$1"
|
||||
if command -v lsof >/dev/null 2>&1; then
|
||||
|
||||
@@ -4,7 +4,7 @@ const http = require('http');
|
||||
const { pipeline } = require('stream');
|
||||
const { URL } = require('url');
|
||||
|
||||
const ROOT_DIR = path.resolve(__dirname, '..');
|
||||
const ROOT_DIR = path.resolve(__dirname, '..', 'dist');
|
||||
const UI_PORT = Number(process.env.CRANK_E2E_UI_PORT || 3300);
|
||||
const ADMIN_PORT = Number(process.env.CRANK_E2E_ADMIN_PORT || 3301);
|
||||
const ADMIN_BASE = new URL(`http://127.0.0.1:${ADMIN_PORT}`);
|
||||
@@ -84,7 +84,7 @@ function mapRoute(urlPath) {
|
||||
return path.join(ROOT_DIR, urlPath.slice(1));
|
||||
}
|
||||
if (urlPath === '/Crank.png') {
|
||||
return path.resolve(ROOT_DIR, '..', '..', 'Crank.png');
|
||||
return path.join(ROOT_DIR, 'Crank.png');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user