cdd-cli 3.1.7 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +1 -1
- package/.github/workflows/ci.yml +3 -0
- package/.github/workflows/docs.yml +8 -1
- package/CHANGELOG.md +40 -0
- package/CONTRIBUTING.md +28 -15
- package/README.md +56 -88
- package/babel.config.cjs +18 -1
- package/coverage/clover.xml +604 -0
- package/coverage/coverage-final.json +17 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/helpers/constants.js.html +235 -0
- package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +211 -0
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +853 -0
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +289 -0
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +131 -0
- package/coverage/lcov-report/helpers/index.html +176 -0
- package/coverage/lcov-report/helpers/logger.js.html +424 -0
- package/coverage/lcov-report/helpers/safeCall.js.html +139 -0
- package/coverage/lcov-report/helpers/validationHelpers.js.html +394 -0
- package/coverage/lcov-report/hooks/creation/index.html +146 -0
- package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +292 -0
- package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +478 -0
- package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +238 -0
- package/coverage/lcov-report/hooks/debug/index.html +116 -0
- package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +172 -0
- package/coverage/lcov-report/hooks/index.html +161 -0
- package/coverage/lcov-report/hooks/navigation/index.html +116 -0
- package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +160 -0
- package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +415 -0
- package/coverage/lcov-report/hooks/useControls.js.html +685 -0
- package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +202 -0
- package/coverage/lcov-report/hooks/useExitHandler.js.html +283 -0
- package/coverage/lcov-report/index.html +191 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +1216 -0
- package/dist/components/ContainerRow.js +5 -78
- package/dist/components/LogViewer.js +4 -9
- package/dist/helpers/containerOptionsBuilder.js +58 -0
- package/dist/hooks/creation/useContainerCreation.js +16 -1
- package/dist/hooks/debug/useDebugLogs.js +46 -0
- package/dist/hooks/navigation/useContainerSelection.js +36 -0
- package/dist/hooks/useContainerCommandRouter.js +151 -0
- package/dist/{helpers/actionHelpers.js → hooks/useContainerStats.js} +76 -78
- package/dist/hooks/useControls.js +83 -342
- package/dist/hooks/useEraseConfirmation.js +47 -0
- package/dist/hooks/useExitHandler.js +68 -0
- package/jest.config.cjs +1 -1
- package/package.json +2 -3
- package/src/components/ContainerRow.jsx +3 -38
- package/src/components/LogViewer.jsx +4 -10
- package/src/helpers/constants.js +20 -0
- package/src/helpers/containerOptionsBuilder.js +42 -0
- package/src/helpers/dockerService/serviceComponents/containerActions.js +55 -8
- package/src/helpers/validationHelpers.js +75 -18
- package/src/hooks/creation/useContainerCreation.js +34 -4
- package/src/hooks/debug/useDebugLogs.js +29 -0
- package/src/hooks/navigation/useContainerSelection.js +25 -0
- package/src/hooks/useContainerCommandRouter.js +110 -0
- package/src/hooks/useContainerStats.js +44 -0
- package/src/hooks/useControls.js +75 -274
- package/src/hooks/useEraseConfirmation.js +39 -0
- package/src/hooks/useExitHandler.js +66 -0
- package/test/containerActions.test.js +104 -13
- package/test/containerOptionsBuilder.test.js +36 -0
- package/test/useContainerCreation.dom.test.js +46 -2
- package/test/useControls.dom.test.js +117 -0
- package/test/validationHelpers.test.js +51 -0
- package/.babelrc +0 -6
- package/FIXES_APPLIED.md +0 -419
- package/SECURITY.md +0 -5
- package/dist/hooks/useLogsStream.js +0 -41
- package/docs/_config.yml +0 -13
- package/docs/components_ContainerCreationPrompt.jsx.html +0 -114
- package/docs/components_ContainerRow.jsx.html +0 -151
- package/docs/components_ContainerSection.jsx.html +0 -75
- package/docs/components_Footer.jsx.html +0 -66
- package/docs/components_Header.jsx.html +0 -84
- package/docs/components_LogViewer.jsx.html +0 -91
- package/docs/components_MessageFeedback.jsx.html +0 -76
- package/docs/components_PromptField.jsx.html +0 -99
- package/docs/components_StatsBar.jsx.html +0 -105
- package/docs/components_UsageMenu.jsx.html +0 -73
- package/docs/fonts/OpenSans-Bold-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Bold-webfont.svg +0 -1830
- package/docs/fonts/OpenSans-Bold-webfont.woff +0 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-BoldItalic-webfont.svg +0 -1830
- package/docs/fonts/OpenSans-BoldItalic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Italic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Italic-webfont.svg +0 -1830
- package/docs/fonts/OpenSans-Italic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Light-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Light-webfont.svg +0 -1831
- package/docs/fonts/OpenSans-Light-webfont.woff +0 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.eot +0 -0
- package/docs/fonts/OpenSans-LightItalic-webfont.svg +0 -1835
- package/docs/fonts/OpenSans-LightItalic-webfont.woff +0 -0
- package/docs/fonts/OpenSans-Regular-webfont.eot +0 -0
- package/docs/fonts/OpenSans-Regular-webfont.svg +0 -1831
- package/docs/fonts/OpenSans-Regular-webfont.woff +0 -0
- package/docs/global.html +0 -6145
- package/docs/helpers_actionHelpers.js.html +0 -94
- package/docs/helpers_constants.js.html +0 -81
- package/docs/helpers_dockerService_dockerService.js.html +0 -79
- package/docs/helpers_dockerService_serviceComponents_containerActions.js.html +0 -153
- package/docs/helpers_dockerService_serviceComponents_containerList.js.html +0 -81
- package/docs/helpers_dockerService_serviceComponents_containerLogs.js.html +0 -85
- package/docs/helpers_dockerService_serviceComponents_containerStats.js.html +0 -107
- package/docs/helpers_dockerService_serviceComponents_imageUtils.js.html +0 -82
- package/docs/helpers_exitWithMessage.js.html +0 -77
- package/docs/helpers_safeCall.js.html +0 -68
- package/docs/helpers_validationHelpers.js.html +0 -96
- package/docs/hooks_creation_useContainerActions.js.html +0 -120
- package/docs/hooks_creation_useContainerCreation.js.html +0 -152
- package/docs/hooks_creation_useLogsViewer.js.html +0 -102
- package/docs/hooks_useControls.js.html +0 -298
- package/docs/hooks_useLogsStream.js.html +0 -82
- package/docs/index.html +0 -65
- package/docs/index.js.html +0 -65
- package/docs/module-index.html +0 -171
- package/docs/scripts/linenumber.js +0 -25
- package/docs/scripts/prettify/Apache-License-2.0.txt +0 -202
- package/docs/scripts/prettify/lang-css.js +0 -2
- package/docs/scripts/prettify/prettify.js +0 -28
- package/docs/styles/jsdoc-default.css +0 -358
- package/docs/styles/prettify-jsdoc.css +0 -111
- package/docs/styles/prettify-tomorrow.css +0 -132
- package/esbuild.config.cjs +0 -11
- package/src/helpers/actionHelpers.js +0 -52
- package/src/hooks/useLogsStream.js +0 -31
- package/test/actionHelpers.test.js +0 -106
|
@@ -13,14 +13,18 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
|
|
|
13
13
|
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
14
14
|
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
15
15
|
import React from "react";
|
|
16
|
+
import { useInput } from "ink";
|
|
16
17
|
import { useContainerActions } from "./creation/useContainerActions.js";
|
|
17
18
|
import { useContainerCreation } from "./creation/useContainerCreation.js";
|
|
18
19
|
import { useLogsViewer } from "./creation/useLogsViewer.js";
|
|
19
|
-
import {
|
|
20
|
+
import { useContainerSelection } from "./navigation/useContainerSelection.js";
|
|
21
|
+
import { useDebugLogs } from "./debug/useDebugLogs.js";
|
|
22
|
+
import { useEraseConfirmation } from "./useEraseConfirmation.js";
|
|
23
|
+
import { useExitHandler } from "./useExitHandler.js";
|
|
24
|
+
import { useContainerCommandRouter } from "./useContainerCommandRouter.js";
|
|
20
25
|
import { getLogsStream } from "../helpers/dockerService/serviceComponents/containerLogs.js";
|
|
21
|
-
import { EXIT_DELAY } from "../helpers/constants.js";
|
|
22
26
|
import { createContainer as svcCreateContainer } from "../helpers/dockerService/serviceComponents/containerActions.js";
|
|
23
|
-
import {
|
|
27
|
+
import { buildContainerOptions } from "../helpers/containerOptionsBuilder.js";
|
|
24
28
|
|
|
25
29
|
// Principal hook to manage user inputs and control the app state
|
|
26
30
|
/**
|
|
@@ -29,95 +33,32 @@ import { logger } from "../helpers/logger.js";
|
|
|
29
33
|
*
|
|
30
34
|
* @param {Array<Object>} containers - Current list of Docker containers
|
|
31
35
|
* @returns {Object} controls - API for the App component
|
|
32
|
-
* @property {number} selected - Index of the currently selected container
|
|
33
|
-
* @property {function} setSelected - Setter for selected index
|
|
34
|
-
* @property {string} message - Current feedback message (creation or actions)
|
|
35
|
-
* @property {string} messageColor - Color to show for the feedback message
|
|
36
|
-
* @property {boolean} showLogs - Whether the logs viewer is active
|
|
37
|
-
* @property {Array<string>} logs - Array of log lines currently collected
|
|
38
|
-
* @property {function} exitLogs - Helper to close the logs viewer
|
|
39
|
-
* @property {boolean} creatingContainer - Whether the create-container prompt is open
|
|
40
|
-
* @property {number} creationStep - Current step in the creation flow
|
|
41
|
-
* @property {string} imageNameInput - Current value of the image name field
|
|
42
|
-
* @property {string} containerNameInput - Current value of the container name field
|
|
43
|
-
* @property {string} portInput - Current value of the port input field
|
|
44
|
-
* @property {string} envInput - Current value of the env input field
|
|
45
|
-
* @property {Object} creation - The creation hook API (setters and helpers)
|
|
46
|
-
* @property {Object} actions - The actions hook API (helpers to start/stop/remove)
|
|
47
|
-
* @property {Object} logsViewer - The logs viewer hook API
|
|
48
|
-
* @property {boolean} showDebugLogs - Whether the debug log panel is visible
|
|
49
|
-
* @property {Array<string>} debugLogs - Buffered log lines captured from the shared logger
|
|
50
|
-
* @property {boolean} confirmErase - Whether the erase confirmation dialog is active
|
|
51
36
|
*/
|
|
52
37
|
export function useControls() {
|
|
53
38
|
var containers = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
|
54
|
-
var _React$useState = React.useState(
|
|
39
|
+
var _React$useState = React.useState(false),
|
|
55
40
|
_React$useState2 = _slicedToArray(_React$useState, 2),
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
var _React$useState3 = React.useState(false),
|
|
59
|
-
_React$useState4 = _slicedToArray(_React$useState3, 2),
|
|
60
|
-
creatingContainer = _React$useState4[0],
|
|
61
|
-
setCreatingContainer = _React$useState4[1];
|
|
62
|
-
var _React$useState5 = React.useState(false),
|
|
63
|
-
_React$useState6 = _slicedToArray(_React$useState5, 2),
|
|
64
|
-
confirmErase = _React$useState6[0],
|
|
65
|
-
setConfirmErase = _React$useState6[1];
|
|
66
|
-
var _React$useState7 = React.useState(false),
|
|
67
|
-
_React$useState8 = _slicedToArray(_React$useState7, 2),
|
|
68
|
-
showDebugLogs = _React$useState8[0],
|
|
69
|
-
setShowDebugLogs = _React$useState8[1];
|
|
70
|
-
var _React$useState9 = React.useState([]),
|
|
71
|
-
_React$useState0 = _slicedToArray(_React$useState9, 2),
|
|
72
|
-
debugLogs = _React$useState0[0],
|
|
73
|
-
setDebugLogs = _React$useState0[1];
|
|
74
|
-
var total = containers.length;
|
|
75
|
-
var _useApp = useApp(),
|
|
76
|
-
exit = _useApp.exit;
|
|
41
|
+
creatingContainer = _React$useState2[0],
|
|
42
|
+
setCreatingContainer = _React$useState2[1];
|
|
77
43
|
|
|
78
|
-
// Modular hooks
|
|
44
|
+
// — Modular hooks —
|
|
79
45
|
var actions = useContainerActions({
|
|
80
46
|
containers: containers
|
|
81
47
|
});
|
|
82
48
|
var creation = useContainerCreation({
|
|
83
49
|
onCreate: function () {
|
|
84
50
|
var _onCreate = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(_ref) {
|
|
85
|
-
var imageName, containerName, portInput, envInput,
|
|
51
|
+
var imageName, containerName, portInput, envInput, options, id, _t;
|
|
86
52
|
return _regenerator().w(function (_context) {
|
|
87
53
|
while (1) switch (_context.p = _context.n) {
|
|
88
54
|
case 0:
|
|
89
55
|
imageName = _ref.imageName, containerName = _ref.containerName, portInput = _ref.portInput, envInput = _ref.envInput;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return s.trim();
|
|
96
|
-
}).filter(Boolean);
|
|
97
|
-
ExposedPorts = {};
|
|
98
|
-
PortBindings = {};
|
|
99
|
-
ports.forEach(function (pair) {
|
|
100
|
-
var _pair$split = pair.split(":"),
|
|
101
|
-
_pair$split2 = _slicedToArray(_pair$split, 2),
|
|
102
|
-
host = _pair$split2[0],
|
|
103
|
-
cont = _pair$split2[1];
|
|
104
|
-
if (!host || !cont) return;
|
|
105
|
-
var key = "".concat(cont, "/tcp");
|
|
106
|
-
ExposedPorts[key] = {};
|
|
107
|
-
PortBindings[key] = PortBindings[key] || [];
|
|
108
|
-
PortBindings[key].push({
|
|
109
|
-
HostPort: "".concat(host)
|
|
110
|
-
});
|
|
56
|
+
options = buildContainerOptions({
|
|
57
|
+
imageName: imageName,
|
|
58
|
+
containerName: containerName,
|
|
59
|
+
portInput: portInput,
|
|
60
|
+
envInput: envInput
|
|
111
61
|
});
|
|
112
|
-
options = {
|
|
113
|
-
Tty: true
|
|
114
|
-
};
|
|
115
|
-
if (Object.keys(ExposedPorts).length) options.ExposedPorts = ExposedPorts;
|
|
116
|
-
if (Object.keys(PortBindings).length) options.HostConfig = {
|
|
117
|
-
PortBindings: PortBindings
|
|
118
|
-
};
|
|
119
|
-
if (env.length) options.Env = env;
|
|
120
|
-
if (containerName) options.name = containerName;
|
|
121
62
|
actions.setMessage("Creating container ".concat(imageName, "..."));
|
|
122
63
|
actions.setMessageColor("yellow");
|
|
123
64
|
_context.p = 1;
|
|
@@ -154,32 +95,10 @@ export function useControls() {
|
|
|
154
95
|
dbImages: ["mysql", "mariadb", "postgres", "mongo", "mssql", "redis"]
|
|
155
96
|
});
|
|
156
97
|
var logsViewer = useLogsViewer();
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
var
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Pipe container logs into the viewer while applying a hard limit.
|
|
163
|
-
* @param {string} containerId - Container identifier used by Docker.
|
|
164
|
-
*/
|
|
165
|
-
var startLogsStream = React.useCallback(function (containerId) {
|
|
166
|
-
getLogsStream(containerId, function (data) {
|
|
167
|
-
return logsViewer.setLogs(function (prev) {
|
|
168
|
-
var newLogs = [].concat(_toConsumableArray(prev), _toConsumableArray(data.split("\n").filter(Boolean)));
|
|
169
|
-
return newLogs.slice(-1000);
|
|
170
|
-
});
|
|
171
|
-
}, function () {}, function (err) {
|
|
172
|
-
return logsViewer.setLogs(function (prev) {
|
|
173
|
-
return [].concat(_toConsumableArray(prev), ["Error: ".concat(err.message)]);
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
}, [logsViewer]);
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Manage the erase confirmation short-circuit flow.
|
|
180
|
-
*/
|
|
181
|
-
var processEraseConfirmation = React.useCallback(function (input, key) {
|
|
182
|
-
if (input === "y" || input === "Y") {
|
|
98
|
+
var selection = useContainerSelection(containers.length);
|
|
99
|
+
var debugLogs = useDebugLogs();
|
|
100
|
+
var eraseConfirmation = useEraseConfirmation({
|
|
101
|
+
onConfirm: function onConfirm() {
|
|
183
102
|
actions.handleAction({
|
|
184
103
|
actionFn: function () {
|
|
185
104
|
var _actionFn = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(id) {
|
|
@@ -199,30 +118,61 @@ export function useControls() {
|
|
|
199
118
|
return actionFn;
|
|
200
119
|
}(),
|
|
201
120
|
actionLabel: "Erasing",
|
|
202
|
-
selected: selected
|
|
121
|
+
selected: selection.selected
|
|
203
122
|
});
|
|
204
|
-
setConfirmErase(false);
|
|
205
123
|
actions.setMessageColor("yellow");
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (input === "n" || input === "N" || key.escape) {
|
|
209
|
-
setConfirmErase(false);
|
|
124
|
+
},
|
|
125
|
+
onCancel: function onCancel() {
|
|
210
126
|
actions.setMessage("");
|
|
211
127
|
actions.setMessageColor("");
|
|
212
|
-
return;
|
|
213
128
|
}
|
|
214
|
-
|
|
215
|
-
actions.setMessageColor("yellow");
|
|
216
|
-
}, [actions, selected]);
|
|
129
|
+
});
|
|
217
130
|
|
|
218
131
|
/**
|
|
219
|
-
*
|
|
132
|
+
* Pipe container logs into the viewer while applying a hard limit.
|
|
133
|
+
* @param {string} containerId - Container identifier used by Docker.
|
|
220
134
|
*/
|
|
221
|
-
var
|
|
222
|
-
|
|
135
|
+
var startLogsStream = React.useCallback(function (containerId) {
|
|
136
|
+
getLogsStream(containerId, function (data) {
|
|
137
|
+
return logsViewer.setLogs(function (prev) {
|
|
138
|
+
var newLogs = [].concat(_toConsumableArray(prev), _toConsumableArray(data.split("\n").filter(Boolean)));
|
|
139
|
+
return newLogs.slice(-1000);
|
|
140
|
+
});
|
|
141
|
+
}, function () {}, function (err) {
|
|
142
|
+
return logsViewer.setLogs(function (prev) {
|
|
143
|
+
return [].concat(_toConsumableArray(prev), ["Error: ".concat(err.message)]);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}, [logsViewer]);
|
|
147
|
+
var commandRouter = useContainerCommandRouter({
|
|
148
|
+
actions: actions,
|
|
149
|
+
containers: containers,
|
|
150
|
+
selected: selection.selected,
|
|
151
|
+
creation: creation,
|
|
152
|
+
logsViewer: logsViewer,
|
|
153
|
+
startLogsStream: startLogsStream,
|
|
154
|
+
onStartErase: function onStartErase() {
|
|
155
|
+
eraseConfirmation.startErase();
|
|
156
|
+
actions.setMessage("Are you sure you want to erase this container? (y/n)");
|
|
157
|
+
actions.setMessageColor("yellow");
|
|
158
|
+
},
|
|
159
|
+
onToggleDebug: function onToggleDebug() {
|
|
160
|
+
return debugLogs.setShowDebugLogs(function (prev) {
|
|
161
|
+
return !prev;
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
onStartCreate: function onStartCreate() {
|
|
165
|
+
return setCreatingContainer(true);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
var exitHandler = useExitHandler({
|
|
169
|
+
onBeforeExit: function onBeforeExit() {
|
|
170
|
+
actions.setMessage("Exiting...");
|
|
171
|
+
actions.setMessageColor("yellow");
|
|
223
172
|
logsViewer.closeLogs();
|
|
173
|
+
debugLogs.setShowDebugLogs(false);
|
|
224
174
|
}
|
|
225
|
-
}
|
|
175
|
+
});
|
|
226
176
|
|
|
227
177
|
/**
|
|
228
178
|
* Route keystrokes to the container creation wizard.
|
|
@@ -261,248 +211,39 @@ export function useControls() {
|
|
|
261
211
|
}
|
|
262
212
|
}, [creation, setCreatingContainer]);
|
|
263
213
|
|
|
264
|
-
|
|
265
|
-
* Navigate the container list using arrow keys.
|
|
266
|
-
* @returns {boolean} True when the input was consumed.
|
|
267
|
-
*/
|
|
268
|
-
var handleNavigation = React.useCallback(function (input, key) {
|
|
269
|
-
if (total === 0) {
|
|
270
|
-
return false;
|
|
271
|
-
}
|
|
272
|
-
if (key.upArrow) {
|
|
273
|
-
setSelected(function (i) {
|
|
274
|
-
return i === 0 ? total - 1 : i - 1;
|
|
275
|
-
});
|
|
276
|
-
return true;
|
|
277
|
-
}
|
|
278
|
-
if (key.downArrow) {
|
|
279
|
-
setSelected(function (i) {
|
|
280
|
-
return i === total - 1 ? 0 : i + 1;
|
|
281
|
-
});
|
|
282
|
-
return true;
|
|
283
|
-
}
|
|
284
|
-
return false;
|
|
285
|
-
}, [total]);
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Handle the quit command.
|
|
289
|
-
* @returns {boolean} True when the input was consumed.
|
|
290
|
-
*/
|
|
291
|
-
var handleExitCommand = React.useCallback(function (input) {
|
|
292
|
-
if (input !== "q") {
|
|
293
|
-
return false;
|
|
294
|
-
}
|
|
295
|
-
actions.setMessage("Exiting...");
|
|
296
|
-
actions.setMessageColor("yellow");
|
|
297
|
-
logsViewer.closeLogs();
|
|
298
|
-
setShowDebugLogs(false);
|
|
299
|
-
var clearTerminal = function clearTerminal() {
|
|
300
|
-
try {
|
|
301
|
-
if (process.stdout && process.stdout.isTTY) {
|
|
302
|
-
process.stdout.write("\x1Bc");
|
|
303
|
-
} else {
|
|
304
|
-
console.clear();
|
|
305
|
-
}
|
|
306
|
-
} catch (err) {
|
|
307
|
-
// Ignore clear errors to avoid blocking exit.
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
setTimeout(function () {
|
|
311
|
-
clearTerminal();
|
|
312
|
-
exit();
|
|
313
|
-
if (process.env.NODE_ENV !== "test") {
|
|
314
|
-
setTimeout(function () {
|
|
315
|
-
return process.exit(0);
|
|
316
|
-
}, 50);
|
|
317
|
-
}
|
|
318
|
-
}, EXIT_DELAY);
|
|
319
|
-
return true;
|
|
320
|
-
}, [actions, exit, logsViewer]);
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Execute Docker-related commands and prompt transitions.
|
|
324
|
-
* @returns {boolean} True when the input was consumed.
|
|
325
|
-
*/
|
|
326
|
-
var handleDockerCommands = React.useCallback(function (input) {
|
|
327
|
-
var container = containers[selected];
|
|
328
|
-
if (input === "i") {
|
|
329
|
-
actions.handleAction({
|
|
330
|
-
actionFn: function () {
|
|
331
|
-
var _actionFn2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(id) {
|
|
332
|
-
return _regenerator().w(function (_context3) {
|
|
333
|
-
while (1) switch (_context3.n) {
|
|
334
|
-
case 0:
|
|
335
|
-
_context3.n = 1;
|
|
336
|
-
return actions.startContainer(id);
|
|
337
|
-
case 1:
|
|
338
|
-
return _context3.a(2, _context3.v);
|
|
339
|
-
}
|
|
340
|
-
}, _callee3);
|
|
341
|
-
}));
|
|
342
|
-
function actionFn(_x3) {
|
|
343
|
-
return _actionFn2.apply(this, arguments);
|
|
344
|
-
}
|
|
345
|
-
return actionFn;
|
|
346
|
-
}(),
|
|
347
|
-
actionLabel: "Starting",
|
|
348
|
-
selected: selected,
|
|
349
|
-
stateCheck: function stateCheck(c) {
|
|
350
|
-
return (c.state === "running" || c.status === "running") && "Container is already running.";
|
|
351
|
-
}
|
|
352
|
-
});
|
|
353
|
-
return true;
|
|
354
|
-
}
|
|
355
|
-
if (input === "p") {
|
|
356
|
-
actions.handleAction({
|
|
357
|
-
actionFn: function () {
|
|
358
|
-
var _actionFn3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(id) {
|
|
359
|
-
return _regenerator().w(function (_context4) {
|
|
360
|
-
while (1) switch (_context4.n) {
|
|
361
|
-
case 0:
|
|
362
|
-
_context4.n = 1;
|
|
363
|
-
return actions.stopContainer(id);
|
|
364
|
-
case 1:
|
|
365
|
-
return _context4.a(2, _context4.v);
|
|
366
|
-
}
|
|
367
|
-
}, _callee4);
|
|
368
|
-
}));
|
|
369
|
-
function actionFn(_x4) {
|
|
370
|
-
return _actionFn3.apply(this, arguments);
|
|
371
|
-
}
|
|
372
|
-
return actionFn;
|
|
373
|
-
}(),
|
|
374
|
-
actionLabel: "Stopping",
|
|
375
|
-
selected: selected,
|
|
376
|
-
stateCheck: function stateCheck(c) {
|
|
377
|
-
return (c.state === "exited" || c.status === "exited" || c.state === "stopped" || c.status === "stopped") && "Container is already stopped.";
|
|
378
|
-
}
|
|
379
|
-
});
|
|
380
|
-
return true;
|
|
381
|
-
}
|
|
382
|
-
if (input === "r") {
|
|
383
|
-
actions.handleAction({
|
|
384
|
-
actionFn: function () {
|
|
385
|
-
var _actionFn4 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(id) {
|
|
386
|
-
return _regenerator().w(function (_context5) {
|
|
387
|
-
while (1) switch (_context5.n) {
|
|
388
|
-
case 0:
|
|
389
|
-
_context5.n = 1;
|
|
390
|
-
return actions.restartContainer(id);
|
|
391
|
-
case 1:
|
|
392
|
-
return _context5.a(2, _context5.v);
|
|
393
|
-
}
|
|
394
|
-
}, _callee5);
|
|
395
|
-
}));
|
|
396
|
-
function actionFn(_x5) {
|
|
397
|
-
return _actionFn4.apply(this, arguments);
|
|
398
|
-
}
|
|
399
|
-
return actionFn;
|
|
400
|
-
}(),
|
|
401
|
-
actionLabel: "Restarting",
|
|
402
|
-
selected: selected
|
|
403
|
-
});
|
|
404
|
-
return true;
|
|
405
|
-
}
|
|
406
|
-
if (input === "e") {
|
|
407
|
-
if (!container) {
|
|
408
|
-
return true;
|
|
409
|
-
}
|
|
410
|
-
setConfirmErase(true);
|
|
411
|
-
actions.setMessage("Are you sure you want to erase this container? (y/n)");
|
|
412
|
-
actions.setMessageColor("yellow");
|
|
413
|
-
return true;
|
|
414
|
-
}
|
|
415
|
-
if (input === "l") {
|
|
416
|
-
if (!container) {
|
|
417
|
-
return true;
|
|
418
|
-
}
|
|
419
|
-
logsViewer.openLogs();
|
|
420
|
-
startLogsStream(container.id);
|
|
421
|
-
return true;
|
|
422
|
-
}
|
|
423
|
-
if (input === "c") {
|
|
424
|
-
setCreatingContainer(true);
|
|
425
|
-
creation.setStep(0);
|
|
426
|
-
creation.setImageName("");
|
|
427
|
-
creation.setContainerName("");
|
|
428
|
-
creation.setPortInput("");
|
|
429
|
-
creation.setEnvInput("");
|
|
430
|
-
creation.setMessage("Insert the name of the image to create: ");
|
|
431
|
-
creation.setMessageColor("yellow");
|
|
432
|
-
return true;
|
|
433
|
-
}
|
|
434
|
-
if (input === "d") {
|
|
435
|
-
setShowDebugLogs(function (prev) {
|
|
436
|
-
return !prev;
|
|
437
|
-
});
|
|
438
|
-
return true;
|
|
439
|
-
}
|
|
440
|
-
return false;
|
|
441
|
-
}, [actions, containers, creation, logsViewer, selected, setConfirmErase, setCreatingContainer, startLogsStream, setShowDebugLogs]);
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Default handler executed when no special mode is active.
|
|
445
|
-
*/
|
|
446
|
-
var processGlobalInput = React.useCallback(function (input, key) {
|
|
447
|
-
if (handleNavigation(input, key)) {
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
if (handleExitCommand(input)) {
|
|
451
|
-
return;
|
|
452
|
-
}
|
|
453
|
-
handleDockerCommands(input);
|
|
454
|
-
}, [handleDockerCommands, handleExitCommand, handleNavigation]);
|
|
214
|
+
// Single keyboard entry point
|
|
455
215
|
useInput(function (input, key) {
|
|
456
|
-
if (confirmErase) {
|
|
457
|
-
processEraseConfirmation(input, key);
|
|
216
|
+
if (eraseConfirmation.confirmErase) {
|
|
217
|
+
eraseConfirmation.processEraseConfirmation(input, key);
|
|
458
218
|
return;
|
|
459
219
|
}
|
|
460
220
|
if (logsViewer.showLogs) {
|
|
461
|
-
|
|
221
|
+
if (input === "q" || key.escape) {
|
|
222
|
+
logsViewer.closeLogs();
|
|
223
|
+
}
|
|
462
224
|
return;
|
|
463
225
|
}
|
|
464
226
|
if (creatingContainer) {
|
|
465
227
|
processCreationInput(input, key);
|
|
466
228
|
return;
|
|
467
229
|
}
|
|
468
|
-
if (showDebugLogs && key.escape) {
|
|
469
|
-
setShowDebugLogs(false);
|
|
230
|
+
if (debugLogs.showDebugLogs && key.escape) {
|
|
231
|
+
debugLogs.setShowDebugLogs(false);
|
|
470
232
|
return;
|
|
471
233
|
}
|
|
472
|
-
|
|
234
|
+
commandRouter.handleDockerCommands(input);
|
|
235
|
+
exitHandler.handleExitCommand(input);
|
|
236
|
+
selection.handleNavigation(input, key);
|
|
473
237
|
});
|
|
474
|
-
|
|
475
|
-
// Mirror global logger entries so the D shortcut can surface them without leaving the CLI.
|
|
476
|
-
React.useEffect(function () {
|
|
477
|
-
var unsubscribe = logger.subscribe(function (entry) {
|
|
478
|
-
setDebugLogs(function (prev) {
|
|
479
|
-
var extras = (entry.args || []).map(function (arg) {
|
|
480
|
-
if (typeof arg === "string") return arg;
|
|
481
|
-
if (typeof arg === "number" || typeof arg === "boolean") return String(arg);
|
|
482
|
-
try {
|
|
483
|
-
return JSON.stringify(arg);
|
|
484
|
-
} catch (err) {
|
|
485
|
-
return "[unserializable]";
|
|
486
|
-
}
|
|
487
|
-
}).filter(Boolean);
|
|
488
|
-
var line = extras.length ? "".concat(entry.formatted, " ").concat(extras.join(" ")) : entry.formatted;
|
|
489
|
-
var next = [].concat(_toConsumableArray(prev), [line]);
|
|
490
|
-
return next.slice(-200);
|
|
491
|
-
});
|
|
492
|
-
});
|
|
493
|
-
return unsubscribe;
|
|
494
|
-
}, []);
|
|
495
238
|
return {
|
|
496
|
-
selected: selected,
|
|
497
|
-
setSelected: setSelected,
|
|
498
|
-
// Map messaging to creation when creating, otherwise to actions
|
|
239
|
+
selected: selection.selected,
|
|
240
|
+
setSelected: selection.setSelected,
|
|
499
241
|
message: creatingContainer ? creation.message : actions.message,
|
|
500
242
|
messageColor: creatingContainer ? creation.messageColor : actions.messageColor,
|
|
501
243
|
showLogs: logsViewer.showLogs,
|
|
502
244
|
logs: logsViewer.logs,
|
|
503
|
-
exitLogs:
|
|
245
|
+
exitLogs: logsViewer.closeLogs,
|
|
504
246
|
creatingContainer: creatingContainer,
|
|
505
|
-
// Expose creation fields in the shape App expects
|
|
506
247
|
creationStep: creation.step,
|
|
507
248
|
imageNameInput: creation.imageName,
|
|
508
249
|
containerNameInput: creation.containerName,
|
|
@@ -511,8 +252,8 @@ export function useControls() {
|
|
|
511
252
|
creation: creation,
|
|
512
253
|
actions: actions,
|
|
513
254
|
logsViewer: logsViewer,
|
|
514
|
-
confirmErase: confirmErase,
|
|
515
|
-
showDebugLogs: showDebugLogs,
|
|
516
|
-
debugLogs: debugLogs
|
|
255
|
+
confirmErase: eraseConfirmation.confirmErase,
|
|
256
|
+
showDebugLogs: debugLogs.showDebugLogs,
|
|
257
|
+
debugLogs: debugLogs.debugLogs
|
|
517
258
|
};
|
|
518
259
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
2
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
3
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
4
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
5
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
6
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
7
|
+
import { useState, useCallback } from "react";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hook to manage the erase confirmation dialog flow.
|
|
11
|
+
*
|
|
12
|
+
* Encapsulates the `confirmErase` state and the input handler that resolves it.
|
|
13
|
+
* The caller is responsible for what happens on confirm/cancel via the callbacks.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} params
|
|
16
|
+
* @param {Function} params.onConfirm - Called when the user presses y/Y. Should trigger the erase action.
|
|
17
|
+
* @param {Function} params.onCancel - Called when the user presses n/N or Escape. Should clean up messages.
|
|
18
|
+
* @returns {{ confirmErase: boolean, startErase: Function, processEraseConfirmation: Function }}
|
|
19
|
+
*/
|
|
20
|
+
export function useEraseConfirmation(_ref) {
|
|
21
|
+
var onConfirm = _ref.onConfirm,
|
|
22
|
+
onCancel = _ref.onCancel;
|
|
23
|
+
var _useState = useState(false),
|
|
24
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
25
|
+
confirmErase = _useState2[0],
|
|
26
|
+
setConfirmErase = _useState2[1];
|
|
27
|
+
var startErase = useCallback(function () {
|
|
28
|
+
setConfirmErase(true);
|
|
29
|
+
}, []);
|
|
30
|
+
var processEraseConfirmation = useCallback(function (input, key) {
|
|
31
|
+
if (input === "y" || input === "Y") {
|
|
32
|
+
onConfirm();
|
|
33
|
+
setConfirmErase(false);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (input === "n" || input === "N" || key.escape) {
|
|
37
|
+
setConfirmErase(false);
|
|
38
|
+
onCancel();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
}, [onConfirm, onCancel]);
|
|
42
|
+
return {
|
|
43
|
+
confirmErase: confirmErase,
|
|
44
|
+
startErase: startErase,
|
|
45
|
+
processEraseConfirmation: processEraseConfirmation
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { useApp } from 'ink';
|
|
3
|
+
import { EXIT_DELAY } from '../helpers/constants.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook that encapsulates the application exit sequence triggered by pressing `q`.
|
|
7
|
+
*
|
|
8
|
+
* Responsibilities:
|
|
9
|
+
* - Detect the `q` keypress
|
|
10
|
+
* - Invoke an optional `onBeforeExit` callback for resource cleanup (e.g. closing logs)
|
|
11
|
+
* - Clear the terminal after a short delay
|
|
12
|
+
* - Call Ink's `exit()` and, outside of test environments, `process.exit(0)`
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} [options]
|
|
15
|
+
* @param {Function} [options.onBeforeExit] - Optional callback invoked synchronously
|
|
16
|
+
* before the exit delay starts. Use it to close logs, reset UI state, etc.
|
|
17
|
+
* @returns {{ handleExitCommand: Function }}
|
|
18
|
+
*/
|
|
19
|
+
export function useExitHandler() {
|
|
20
|
+
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
21
|
+
onBeforeExit = _ref.onBeforeExit;
|
|
22
|
+
var _useApp = useApp(),
|
|
23
|
+
exit = _useApp.exit;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Handle the quit command.
|
|
27
|
+
*
|
|
28
|
+
* Mirrors the exact logic of `handleExitCommand` in `useControls.js`:
|
|
29
|
+
* 1. Guard: return false if input is not 'q'
|
|
30
|
+
* 2. Call onBeforeExit (replaces inline logsViewer.closeLogs + setShowDebugLogs(false))
|
|
31
|
+
* 3. After EXIT_DELAY: clear terminal, call exit(), then process.exit(0) outside tests
|
|
32
|
+
*
|
|
33
|
+
* @param {string} input - Raw character input from Ink's useInput
|
|
34
|
+
* @returns {boolean} True when the input was consumed, false otherwise
|
|
35
|
+
*/
|
|
36
|
+
var handleExitCommand = useCallback(function (input) {
|
|
37
|
+
if (input !== 'q') {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
if (typeof onBeforeExit === 'function') {
|
|
41
|
+
onBeforeExit();
|
|
42
|
+
}
|
|
43
|
+
var clearTerminal = function clearTerminal() {
|
|
44
|
+
try {
|
|
45
|
+
if (process.stdout && process.stdout.isTTY) {
|
|
46
|
+
process.stdout.write("\x1Bc");
|
|
47
|
+
} else {
|
|
48
|
+
console.clear();
|
|
49
|
+
}
|
|
50
|
+
} catch (err) {
|
|
51
|
+
// Ignore clear errors to avoid blocking exit.
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
setTimeout(function () {
|
|
55
|
+
clearTerminal();
|
|
56
|
+
exit();
|
|
57
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
58
|
+
setTimeout(function () {
|
|
59
|
+
return process.exit(0);
|
|
60
|
+
}, 50);
|
|
61
|
+
}
|
|
62
|
+
}, EXIT_DELAY);
|
|
63
|
+
return true;
|
|
64
|
+
}, [exit, onBeforeExit]);
|
|
65
|
+
return {
|
|
66
|
+
handleExitCommand: handleExitCommand
|
|
67
|
+
};
|
|
68
|
+
}
|
package/jest.config.cjs
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdd-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "CLI Docker Dashboard",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "node --experimental-vm-modules ./node_modules/.bin/jest --runInBand",
|
|
8
|
-
"build": "babel src --out-dir dist --extensions .js,.jsx && node fix-imports.cjs",
|
|
8
|
+
"build": "NODE_ENV=production babel src --out-dir dist --extensions .js,.jsx && node fix-imports.cjs",
|
|
9
9
|
"changes": "npm run build && node dist/index.js",
|
|
10
10
|
"lint": "eslint src --ext .js,.jsx",
|
|
11
11
|
"docs": "jsdoc -r -d docs src"
|
|
@@ -34,7 +34,6 @@
|
|
|
34
34
|
"@testing-library/react": "^16.3.0",
|
|
35
35
|
"babel-jest": "^29.6.1",
|
|
36
36
|
"babel-plugin-module-resolver": "^5.0.2",
|
|
37
|
-
"esbuild": "^0.25.11",
|
|
38
37
|
"eslint": "^8.50.0",
|
|
39
38
|
"eslint-plugin-react": "^7.37.5",
|
|
40
39
|
"jest": "^29.6.1",
|