cdd-cli 3.2.2 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.es.md +222 -0
- package/README.md +108 -26
- package/dist/App.js +7 -1
- package/dist/components/ContainerCreationPrompt.js +26 -10
- package/dist/components/ControlsHUD.js +106 -0
- package/dist/components/SuggestionPanel.js +16 -7
- package/dist/components/UsageMenu.js +1 -1
- package/dist/helpers/appState.js +26 -0
- package/dist/helpers/constants.js +60 -21
- package/dist/helpers/dockerHubService.js +111 -0
- package/dist/helpers/dockerService/serviceComponents/containerExec.js +211 -0
- package/dist/helpers/logger.js +1 -1
- package/dist/hooks/creation/useContainerActions.js +22 -5
- package/dist/hooks/creation/useContainerCreation.js +414 -107
- package/dist/hooks/useContainerCommandRouter.js +13 -3
- package/dist/hooks/useControls.js +28 -7
- package/dist/hooks/useShellMode.js +101 -0
- package/dist/index.js +10 -1
- package/package.json +4 -1
- package/.atl/skill-registry.md +0 -30
- package/.editorconfig +0 -12
- package/.eslintrc.json +0 -30
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -30
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
- package/.github/PULL_REQUEST_TEMPLATE/pull_request.md +0 -14
- package/.github/workflows/ci.yml +0 -32
- package/.github/workflows/docs.yml +0 -42
- package/.prettierignore +0 -4
- package/.prettierrc +0 -8
- package/CHANGELOG.md +0 -165
- package/CODE_OF_CONDUCT.md +0 -12
- package/CONTRIBUTING.md +0 -35
- package/SECURITY.md +0 -5
- package/__mocks__/ink.cjs +0 -17
- package/_config.yml +0 -13
- package/babel.config.cjs +0 -23
- package/coverage/clover.xml +0 -694
- package/coverage/coverage-final.json +0 -21
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/components/ContainerCreationPrompt.jsx.html +0 -376
- package/coverage/lcov-report/components/PromptField.jsx.html +0 -229
- package/coverage/lcov-report/components/SuggestionPanel.jsx.html +0 -244
- package/coverage/lcov-report/components/index.html +0 -146
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/helpers/constants.js.html +0 -298
- package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +0 -244
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +0 -931
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +0 -307
- package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +0 -131
- package/coverage/lcov-report/helpers/imageNameUtils.js.html +0 -181
- package/coverage/lcov-report/helpers/index.html +0 -191
- package/coverage/lcov-report/helpers/logger.js.html +0 -424
- package/coverage/lcov-report/helpers/safeCall.js.html +0 -139
- package/coverage/lcov-report/helpers/validationHelpers.js.html +0 -346
- package/coverage/lcov-report/hooks/creation/index.html +0 -146
- package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +0 -313
- package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +0 -805
- package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +0 -238
- package/coverage/lcov-report/hooks/debug/index.html +0 -116
- package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +0 -187
- package/coverage/lcov-report/hooks/index.html +0 -161
- package/coverage/lcov-report/hooks/navigation/index.html +0 -116
- package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +0 -169
- package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +0 -454
- package/coverage/lcov-report/hooks/useControls.js.html +0 -826
- package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +0 -202
- package/coverage/lcov-report/hooks/useExitHandler.js.html +0 -292
- package/coverage/lcov-report/index.html +0 -206
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov.info +0 -1402
- package/fix-imports.cjs +0 -54
- package/jest.config.cjs +0 -14
- package/src/App.jsx +0 -96
- package/src/components/ContainerCreationPrompt.jsx +0 -97
- package/src/components/ContainerList.jsx +0 -32
- package/src/components/ContainerRow.jsx +0 -84
- package/src/components/ContainerSection.jsx +0 -24
- package/src/components/Footer.jsx +0 -15
- package/src/components/Header.jsx +0 -41
- package/src/components/LogViewer.jsx +0 -36
- package/src/components/MessageFeedback.jsx +0 -25
- package/src/components/PromptField.jsx +0 -48
- package/src/components/StatsBar.jsx +0 -70
- package/src/components/SuggestionPanel.jsx +0 -53
- package/src/components/UsageMenu.jsx +0 -52
- package/src/helpers/appInfo.js +0 -23
- package/src/helpers/constants.js +0 -71
- package/src/helpers/containerOptionsBuilder.js +0 -53
- package/src/helpers/dockerService/dockerService.js +0 -28
- package/src/helpers/dockerService/serviceComponents/containerActions.js +0 -282
- package/src/helpers/dockerService/serviceComponents/containerList.js +0 -74
- package/src/helpers/dockerService/serviceComponents/containerLogs.js +0 -60
- package/src/helpers/dockerService/serviceComponents/containerStats.js +0 -69
- package/src/helpers/dockerService/serviceComponents/imageUtils.js +0 -32
- package/src/helpers/exitWithMessage.js +0 -32
- package/src/helpers/imageNameUtils.js +0 -32
- package/src/helpers/logger.js +0 -113
- package/src/helpers/safeCall.js +0 -18
- package/src/helpers/validationHelpers.js +0 -87
- package/src/hooks/creation/useContainerActions.js +0 -76
- package/src/hooks/creation/useContainerCreation.js +0 -240
- package/src/hooks/creation/useLogsViewer.js +0 -51
- package/src/hooks/debug/useDebugLogs.js +0 -34
- package/src/hooks/navigation/useContainerSelection.js +0 -28
- package/src/hooks/useContainerCommandRouter.js +0 -123
- package/src/hooks/useContainerStats.js +0 -44
- package/src/hooks/useContainers.js +0 -26
- package/src/hooks/useControls.js +0 -247
- package/src/hooks/useEraseConfirmation.js +0 -39
- package/src/hooks/useExitHandler.js +0 -69
- package/src/index.js +0 -16
- package/test/ContainerCreationPrompt.dom.test.js +0 -68
- package/test/SuggestionPanel.dom.test.js +0 -53
- package/test/constants.test.js +0 -34
- package/test/containerActions.test.js +0 -229
- package/test/containerList.test.js +0 -79
- package/test/containerOptionsBuilder.test.js +0 -36
- package/test/jest.setup.js +0 -7
- package/test/safeCall.test.js +0 -25
- package/test/useContainerCreation.dom.test.js +0 -264
- package/test/useControls.dom.test.js +0 -313
- package/test/validationHelpers.test.js +0 -111
|
@@ -1,14 +1,52 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
|
|
3
|
+
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
|
|
4
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
5
|
+
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
1
6
|
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
2
7
|
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
8
|
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
9
|
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
10
|
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
11
|
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
7
|
-
|
|
12
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
13
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
14
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
15
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
16
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
17
|
+
import { useReducer, useState, useRef, useEffect } from 'react';
|
|
8
18
|
import { validatePorts, validateEnvVars } from '../../helpers/validationHelpers.js';
|
|
9
|
-
import { IMAGE_PROFILES } from '../../helpers/constants.js';
|
|
19
|
+
import { IMAGE_PROFILES, resolveImageTag } from '../../helpers/constants.js';
|
|
10
20
|
import { safeCall } from '../../helpers/safeCall.js';
|
|
21
|
+
import { searchDockerHub, formatHubResult } from '../../helpers/dockerHubService.js';
|
|
11
22
|
var MAX_VISIBLE = 6;
|
|
23
|
+
var INITIAL_FORM = {
|
|
24
|
+
step: 0,
|
|
25
|
+
imageName: '',
|
|
26
|
+
containerName: '',
|
|
27
|
+
portInput: '',
|
|
28
|
+
envInput: '',
|
|
29
|
+
message: '',
|
|
30
|
+
messageColor: 'yellow',
|
|
31
|
+
suggestions: [],
|
|
32
|
+
selectedSuggestionIndex: -1,
|
|
33
|
+
visibleOffset: 0
|
|
34
|
+
};
|
|
35
|
+
function formReducer(state, action) {
|
|
36
|
+
switch (action.type) {
|
|
37
|
+
case 'RESET':
|
|
38
|
+
return _objectSpread({}, INITIAL_FORM);
|
|
39
|
+
case 'RESET_WIZARD':
|
|
40
|
+
return _objectSpread(_objectSpread({}, INITIAL_FORM), {}, {
|
|
41
|
+
message: 'Insert the name of the image to create: ',
|
|
42
|
+
messageColor: 'yellow'
|
|
43
|
+
});
|
|
44
|
+
case 'SET':
|
|
45
|
+
return _objectSpread(_objectSpread({}, state), action.payload);
|
|
46
|
+
default:
|
|
47
|
+
return state;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
12
50
|
|
|
13
51
|
/**
|
|
14
52
|
* Custom hook to manage the container creation flow, step by step.
|
|
@@ -22,76 +60,230 @@ var MAX_VISIBLE = 6;
|
|
|
22
60
|
* @returns {Object} Creation state, setters, and helpers
|
|
23
61
|
*/
|
|
24
62
|
export function useContainerCreation(_ref) {
|
|
63
|
+
var _imageProfiles$baseNa, _imageProfiles$baseNa2;
|
|
25
64
|
var onCreate = _ref.onCreate,
|
|
26
65
|
onCancel = _ref.onCancel,
|
|
27
66
|
_ref$dbImages = _ref.dbImages,
|
|
28
67
|
dbImages = _ref$dbImages === void 0 ? [] : _ref$dbImages,
|
|
29
68
|
_ref$imageProfiles = _ref.imageProfiles,
|
|
30
69
|
imageProfiles = _ref$imageProfiles === void 0 ? IMAGE_PROFILES : _ref$imageProfiles;
|
|
31
|
-
var
|
|
70
|
+
var _useReducer = useReducer(formReducer, INITIAL_FORM),
|
|
71
|
+
_useReducer2 = _slicedToArray(_useReducer, 2),
|
|
72
|
+
form = _useReducer2[0],
|
|
73
|
+
dispatch = _useReducer2[1];
|
|
74
|
+
var step = form.step,
|
|
75
|
+
imageName = form.imageName,
|
|
76
|
+
containerName = form.containerName,
|
|
77
|
+
portInput = form.portInput,
|
|
78
|
+
envInput = form.envInput,
|
|
79
|
+
message = form.message,
|
|
80
|
+
messageColor = form.messageColor,
|
|
81
|
+
suggestions = form.suggestions,
|
|
82
|
+
selectedSuggestionIndex = form.selectedSuggestionIndex,
|
|
83
|
+
visibleOffset = form.visibleOffset;
|
|
84
|
+
|
|
85
|
+
// Convenience setters that mirror the old useState API
|
|
86
|
+
var setStep = function setStep(v) {
|
|
87
|
+
return dispatch({
|
|
88
|
+
type: 'SET',
|
|
89
|
+
payload: {
|
|
90
|
+
step: v
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
var setImageName = function setImageName(v) {
|
|
95
|
+
return dispatch({
|
|
96
|
+
type: 'SET',
|
|
97
|
+
payload: {
|
|
98
|
+
imageName: v
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
var setContainerName = function setContainerName(v) {
|
|
103
|
+
return dispatch({
|
|
104
|
+
type: 'SET',
|
|
105
|
+
payload: {
|
|
106
|
+
containerName: v
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
var setPortInput = function setPortInput(v) {
|
|
111
|
+
return dispatch({
|
|
112
|
+
type: 'SET',
|
|
113
|
+
payload: {
|
|
114
|
+
portInput: v
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
var setEnvInput = function setEnvInput(v) {
|
|
119
|
+
return dispatch({
|
|
120
|
+
type: 'SET',
|
|
121
|
+
payload: {
|
|
122
|
+
envInput: typeof v === 'function' ? v(form.envInput) : v
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
var setMessage = function setMessage(v) {
|
|
127
|
+
return dispatch({
|
|
128
|
+
type: 'SET',
|
|
129
|
+
payload: {
|
|
130
|
+
message: v
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
};
|
|
134
|
+
var setMessageColor = function setMessageColor(v) {
|
|
135
|
+
return dispatch({
|
|
136
|
+
type: 'SET',
|
|
137
|
+
payload: {
|
|
138
|
+
messageColor: v
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
var setSuggestions = function setSuggestions(v) {
|
|
143
|
+
return dispatch({
|
|
144
|
+
type: 'SET',
|
|
145
|
+
payload: {
|
|
146
|
+
suggestions: v
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
var setSelectedSuggestionIndex = function setSelectedSuggestionIndex(fn) {
|
|
151
|
+
// Support both direct value and updater function
|
|
152
|
+
if (typeof fn === 'function') {
|
|
153
|
+
dispatch({
|
|
154
|
+
type: 'SET',
|
|
155
|
+
payload: {
|
|
156
|
+
selectedSuggestionIndex: fn(form.selectedSuggestionIndex)
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
} else {
|
|
160
|
+
dispatch({
|
|
161
|
+
type: 'SET',
|
|
162
|
+
payload: {
|
|
163
|
+
selectedSuggestionIndex: fn
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
var setVisibleOffset = function setVisibleOffset(fn) {
|
|
169
|
+
if (typeof fn === 'function') {
|
|
170
|
+
dispatch({
|
|
171
|
+
type: 'SET',
|
|
172
|
+
payload: {
|
|
173
|
+
visibleOffset: fn(form.visibleOffset)
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
} else {
|
|
177
|
+
dispatch({
|
|
178
|
+
type: 'SET',
|
|
179
|
+
payload: {
|
|
180
|
+
visibleOffset: fn
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Docker Hub search state (kept as useState because they are independent of form)
|
|
187
|
+
var _useState = useState(false),
|
|
32
188
|
_useState2 = _slicedToArray(_useState, 2),
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
var _useState3 = useState(
|
|
189
|
+
isSearchingHub = _useState2[0],
|
|
190
|
+
setIsSearchingHub = _useState2[1];
|
|
191
|
+
var _useState3 = useState(null),
|
|
36
192
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
_useState8 = _slicedToArray(_useState7, 2),
|
|
45
|
-
portInput = _useState8[0],
|
|
46
|
-
setPortInput = _useState8[1];
|
|
47
|
-
var _useState9 = useState(''),
|
|
48
|
-
_useState0 = _slicedToArray(_useState9, 2),
|
|
49
|
-
envInput = _useState0[0],
|
|
50
|
-
setEnvInput = _useState0[1];
|
|
51
|
-
var _useState1 = useState(''),
|
|
52
|
-
_useState10 = _slicedToArray(_useState1, 2),
|
|
53
|
-
message = _useState10[0],
|
|
54
|
-
setMessage = _useState10[1];
|
|
55
|
-
var _useState11 = useState('yellow'),
|
|
56
|
-
_useState12 = _slicedToArray(_useState11, 2),
|
|
57
|
-
messageColor = _useState12[0],
|
|
58
|
-
setMessageColor = _useState12[1];
|
|
193
|
+
hubResults = _useState4[0],
|
|
194
|
+
setHubResults = _useState4[1];
|
|
195
|
+
// Tracks active request: { controller: AbortController|null, requestId: number }
|
|
196
|
+
var activeHubRequestRef = useRef({
|
|
197
|
+
controller: null,
|
|
198
|
+
requestId: 0
|
|
199
|
+
});
|
|
59
200
|
|
|
60
|
-
//
|
|
61
|
-
var
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
201
|
+
// Timer for auto-clearing messages
|
|
202
|
+
var messageTimerRef = useRef(null);
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Sets a permanent message (e.g. step instructions). No auto-clear.
|
|
206
|
+
*/
|
|
207
|
+
function setStepMessage(msg) {
|
|
208
|
+
var color = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'yellow';
|
|
209
|
+
clearTimeout(messageTimerRef.current);
|
|
210
|
+
dispatch({
|
|
211
|
+
type: 'SET',
|
|
212
|
+
payload: {
|
|
213
|
+
message: msg,
|
|
214
|
+
messageColor: color
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Sets a transient feedback message (e.g. validation errors). Auto-clears after `ms`.
|
|
221
|
+
*/
|
|
222
|
+
function setTimedMessage(msg) {
|
|
223
|
+
var color = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'yellow';
|
|
224
|
+
var ms = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 4000;
|
|
225
|
+
clearTimeout(messageTimerRef.current);
|
|
226
|
+
dispatch({
|
|
227
|
+
type: 'SET',
|
|
228
|
+
payload: {
|
|
229
|
+
message: msg,
|
|
230
|
+
messageColor: color
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
if (msg) {
|
|
234
|
+
messageTimerRef.current = setTimeout(function () {
|
|
235
|
+
return dispatch({
|
|
236
|
+
type: 'SET',
|
|
237
|
+
payload: {
|
|
238
|
+
message: ''
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}, ms);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
useEffect(function () {
|
|
245
|
+
return function () {
|
|
246
|
+
return clearTimeout(messageTimerRef.current);
|
|
247
|
+
};
|
|
248
|
+
}, []);
|
|
73
249
|
|
|
74
250
|
/**
|
|
75
251
|
* Updates the image name input and recalculates autocomplete suggestions.
|
|
76
252
|
* Resets selectedSuggestionIndex to -1 on every keystroke.
|
|
253
|
+
* Also aborts any in-flight Hub search and clears Hub results.
|
|
77
254
|
*
|
|
78
255
|
* @param {string} value - New raw image name typed by user
|
|
79
256
|
*/
|
|
80
257
|
function updateImageInput(value) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
258
|
+
var _activeHubRequestRef$;
|
|
259
|
+
// Abort any in-flight Hub search and clear its state
|
|
260
|
+
(_activeHubRequestRef$ = activeHubRequestRef.current.controller) === null || _activeHubRequestRef$ === void 0 || _activeHubRequestRef$.abort();
|
|
261
|
+
activeHubRequestRef.current.requestId += 1;
|
|
262
|
+
setIsSearchingHub(false);
|
|
263
|
+
setHubResults(null);
|
|
88
264
|
var lower = value.toLowerCase();
|
|
89
|
-
var matches = Object.keys(imageProfiles).filter(function (key) {
|
|
265
|
+
var matches = value.trim() ? Object.keys(imageProfiles).filter(function (key) {
|
|
90
266
|
return key.includes(lower);
|
|
267
|
+
}) : [];
|
|
268
|
+
dispatch({
|
|
269
|
+
type: 'SET',
|
|
270
|
+
payload: {
|
|
271
|
+
imageName: value,
|
|
272
|
+
selectedSuggestionIndex: -1,
|
|
273
|
+
visibleOffset: 0,
|
|
274
|
+
suggestions: matches
|
|
275
|
+
}
|
|
91
276
|
});
|
|
92
|
-
setSuggestions(matches);
|
|
93
277
|
}
|
|
94
278
|
|
|
279
|
+
/**
|
|
280
|
+
* Triggers a Docker Hub search for the current imageName.
|
|
281
|
+
* Guards: imageName must be non-empty; no concurrent search allowed.
|
|
282
|
+
* Uses AbortController + requestId to handle race conditions.
|
|
283
|
+
*/
|
|
284
|
+
function triggerHubSearch() {
|
|
285
|
+
return _triggerHubSearch.apply(this, arguments);
|
|
286
|
+
}
|
|
95
287
|
/**
|
|
96
288
|
* Moves the suggestion selection up (-1) or down (+1).
|
|
97
289
|
* Clamps index to [-1, suggestions.length - 1].
|
|
@@ -99,16 +291,91 @@ export function useContainerCreation(_ref) {
|
|
|
99
291
|
*
|
|
100
292
|
* @param {number} direction - -1 (up) or 1 (down)
|
|
101
293
|
*/
|
|
294
|
+
function _triggerHubSearch() {
|
|
295
|
+
_triggerHubSearch = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
|
|
296
|
+
var _activeHubRequestRef$2;
|
|
297
|
+
var query, requestId, controller, results, _t;
|
|
298
|
+
return _regenerator().w(function (_context) {
|
|
299
|
+
while (1) switch (_context.p = _context.n) {
|
|
300
|
+
case 0:
|
|
301
|
+
query = imageName.trim();
|
|
302
|
+
if (query) {
|
|
303
|
+
_context.n = 1;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
return _context.a(2);
|
|
307
|
+
case 1:
|
|
308
|
+
if (!isSearchingHub) {
|
|
309
|
+
_context.n = 2;
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
return _context.a(2);
|
|
313
|
+
case 2:
|
|
314
|
+
// Abort previous request (should already be aborted from updateImageInput,
|
|
315
|
+
// but guard here too)
|
|
316
|
+
(_activeHubRequestRef$2 = activeHubRequestRef.current.controller) === null || _activeHubRequestRef$2 === void 0 || _activeHubRequestRef$2.abort();
|
|
317
|
+
requestId = activeHubRequestRef.current.requestId + 1;
|
|
318
|
+
activeHubRequestRef.current.requestId = requestId;
|
|
319
|
+
controller = new AbortController();
|
|
320
|
+
activeHubRequestRef.current.controller = controller;
|
|
321
|
+
setIsSearchingHub(true);
|
|
322
|
+
dispatch({
|
|
323
|
+
type: 'SET',
|
|
324
|
+
payload: {
|
|
325
|
+
selectedSuggestionIndex: -1,
|
|
326
|
+
visibleOffset: 0
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
_context.p = 3;
|
|
330
|
+
_context.n = 4;
|
|
331
|
+
return searchDockerHub(query, {
|
|
332
|
+
signal: controller.signal
|
|
333
|
+
});
|
|
334
|
+
case 4:
|
|
335
|
+
results = _context.v;
|
|
336
|
+
if (!(activeHubRequestRef.current.requestId !== requestId)) {
|
|
337
|
+
_context.n = 5;
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
return _context.a(2);
|
|
341
|
+
case 5:
|
|
342
|
+
setHubResults(results.map(formatHubResult));
|
|
343
|
+
_context.n = 8;
|
|
344
|
+
break;
|
|
345
|
+
case 6:
|
|
346
|
+
_context.p = 6;
|
|
347
|
+
_t = _context.v;
|
|
348
|
+
if (!(activeHubRequestRef.current.requestId !== requestId)) {
|
|
349
|
+
_context.n = 7;
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
return _context.a(2);
|
|
353
|
+
case 7:
|
|
354
|
+
// Silence AbortError; for all other errors fall back to static suggestions
|
|
355
|
+
setHubResults(null);
|
|
356
|
+
case 8:
|
|
357
|
+
_context.p = 8;
|
|
358
|
+
if (activeHubRequestRef.current.requestId === requestId) {
|
|
359
|
+
setIsSearchingHub(false);
|
|
360
|
+
}
|
|
361
|
+
return _context.f(8);
|
|
362
|
+
case 9:
|
|
363
|
+
return _context.a(2);
|
|
364
|
+
}
|
|
365
|
+
}, _callee, null, [[3, 6, 8, 9]]);
|
|
366
|
+
}));
|
|
367
|
+
return _triggerHubSearch.apply(this, arguments);
|
|
368
|
+
}
|
|
102
369
|
function moveSuggestionSelection(direction) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
370
|
+
var next = Math.max(-1, Math.min(suggestions.length - 1, selectedSuggestionIndex + direction));
|
|
371
|
+
var newOffset = visibleOffset;
|
|
372
|
+
if (next < visibleOffset) newOffset = Math.max(0, next);else if (next >= visibleOffset + MAX_VISIBLE) newOffset = next - MAX_VISIBLE + 1;
|
|
373
|
+
dispatch({
|
|
374
|
+
type: 'SET',
|
|
375
|
+
payload: {
|
|
376
|
+
selectedSuggestionIndex: next,
|
|
377
|
+
visibleOffset: newOffset
|
|
378
|
+
}
|
|
112
379
|
});
|
|
113
380
|
}
|
|
114
381
|
|
|
@@ -119,10 +386,15 @@ export function useContainerCreation(_ref) {
|
|
|
119
386
|
function applyFocusedSuggestion() {
|
|
120
387
|
if (selectedSuggestionIndex < 0 || selectedSuggestionIndex >= suggestions.length) return;
|
|
121
388
|
var chosen = suggestions[selectedSuggestionIndex];
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
389
|
+
dispatch({
|
|
390
|
+
type: 'SET',
|
|
391
|
+
payload: {
|
|
392
|
+
imageName: resolveImageTag(chosen, imageProfiles),
|
|
393
|
+
suggestions: [],
|
|
394
|
+
selectedSuggestionIndex: -1,
|
|
395
|
+
visibleOffset: 0
|
|
396
|
+
}
|
|
397
|
+
});
|
|
126
398
|
}
|
|
127
399
|
|
|
128
400
|
/**
|
|
@@ -131,48 +403,57 @@ export function useContainerCreation(_ref) {
|
|
|
131
403
|
function nextStep() {
|
|
132
404
|
if (step === 0) {
|
|
133
405
|
if (!imageName.trim()) {
|
|
134
|
-
|
|
135
|
-
setMessageColor('red');
|
|
406
|
+
setTimedMessage('Image name cannot be empty.', 'red');
|
|
136
407
|
return;
|
|
137
408
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
409
|
+
var resolved = resolveImageTag(imageName, imageProfiles);
|
|
410
|
+
dispatch({
|
|
411
|
+
type: 'SET',
|
|
412
|
+
payload: {
|
|
413
|
+
imageName: resolved,
|
|
414
|
+
step: 1
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
setStepMessage('Optional: Enter container name or leave empty and press Enter', 'yellow');
|
|
141
418
|
return;
|
|
142
419
|
}
|
|
143
420
|
if (step === 1) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
421
|
+
dispatch({
|
|
422
|
+
type: 'SET',
|
|
423
|
+
payload: {
|
|
424
|
+
step: 2
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
setStepMessage('Optional: Enter ports (format 8080:80,443:443) or leave empty and press Enter', 'yellow');
|
|
147
428
|
return;
|
|
148
429
|
}
|
|
149
430
|
if (step === 2) {
|
|
150
431
|
// Ports are now optional - only validate if provided
|
|
151
432
|
if (portInput.trim() && !validatePorts(portInput)) {
|
|
152
|
-
|
|
153
|
-
setMessageColor('red');
|
|
433
|
+
setTimedMessage('Port format must be host:container and both must be numbers (e.g. 8080:80)', 'red');
|
|
154
434
|
return;
|
|
155
435
|
}
|
|
156
|
-
|
|
436
|
+
dispatch({
|
|
437
|
+
type: 'SET',
|
|
438
|
+
payload: {
|
|
439
|
+
step: 3
|
|
440
|
+
}
|
|
441
|
+
});
|
|
157
442
|
var isDb = dbImages.some(function (db) {
|
|
158
443
|
return imageName.trim().toLowerCase().includes(db);
|
|
159
444
|
});
|
|
160
445
|
// Check if the image has a profile with required env vars
|
|
161
|
-
var
|
|
162
|
-
var profile = imageProfiles[
|
|
446
|
+
var _baseName = imageName.trim().toLowerCase().split(':')[0].split('/').pop();
|
|
447
|
+
var profile = imageProfiles[_baseName];
|
|
163
448
|
if (profile && profile.requiredEnv && profile.requiredEnv.length) {
|
|
164
449
|
var suggestedPart = profile.suggestedEnv && profile.suggestedEnv.length ? " | Suggested: ".concat(profile.suggestedEnv.join(', ')) : '';
|
|
165
|
-
|
|
166
|
-
setMessageColor('yellow');
|
|
450
|
+
setStepMessage("Required env vars for ".concat(_baseName, ": ").concat(profile.requiredEnv.join(', '), ". Enter as VAR=val,VAR2=val2").concat(suggestedPart), 'yellow');
|
|
167
451
|
} else if (profile && profile.suggestedEnv && profile.suggestedEnv.length) {
|
|
168
|
-
|
|
169
|
-
setMessageColor('yellow');
|
|
452
|
+
setStepMessage("Suggested env vars for ".concat(_baseName, ": ").concat(profile.suggestedEnv.join(', '), ". Enter as VAR=val,VAR2=val2 or leave empty."), 'yellow');
|
|
170
453
|
} else if (isDb) {
|
|
171
|
-
|
|
172
|
-
setMessageColor('yellow');
|
|
454
|
+
setStepMessage('Warning: This image usually requires environment variables (e.g. MYSQL_ROOT_PASSWORD=my-secret-pw for MySQL, POSTGRES_PASSWORD=yourpassword for Postgres). Enter them as VAR=val,VAR2=val2 or leave empty and press Enter.', 'yellow');
|
|
173
455
|
} else {
|
|
174
|
-
|
|
175
|
-
setMessageColor('yellow');
|
|
456
|
+
setStepMessage('Optional: Enter environment variables (format VAR1=val1,VAR2=val2) or leave empty and press Enter', 'yellow');
|
|
176
457
|
}
|
|
177
458
|
return;
|
|
178
459
|
}
|
|
@@ -180,8 +461,7 @@ export function useContainerCreation(_ref) {
|
|
|
180
461
|
// Contextual env validation using image profiles
|
|
181
462
|
var result = validateEnvVars(envInput, imageName, imageProfiles);
|
|
182
463
|
if (!result.valid) {
|
|
183
|
-
|
|
184
|
-
setMessageColor('red');
|
|
464
|
+
setTimedMessage(result.errors.join(' | '), 'red');
|
|
185
465
|
return;
|
|
186
466
|
}
|
|
187
467
|
// Final step: call onCreate with all data (safely)
|
|
@@ -198,16 +478,9 @@ export function useContainerCreation(_ref) {
|
|
|
198
478
|
* Cancels the creation process and resets state.
|
|
199
479
|
*/
|
|
200
480
|
function cancelCreation() {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
setPortInput('');
|
|
205
|
-
setEnvInput('');
|
|
206
|
-
setMessage('');
|
|
207
|
-
setMessageColor('yellow');
|
|
208
|
-
setSuggestions([]);
|
|
209
|
-
setSelectedSuggestionIndex(-1);
|
|
210
|
-
setVisibleOffset(0);
|
|
481
|
+
dispatch({
|
|
482
|
+
type: 'RESET'
|
|
483
|
+
});
|
|
211
484
|
safeCall(onCancel);
|
|
212
485
|
}
|
|
213
486
|
|
|
@@ -216,17 +489,46 @@ export function useContainerCreation(_ref) {
|
|
|
216
489
|
* Used by the command router when the user presses 'c' to open the creation wizard.
|
|
217
490
|
*/
|
|
218
491
|
function resetCreation() {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
setPortInput('');
|
|
223
|
-
setEnvInput('');
|
|
224
|
-
setMessage('Insert the name of the image to create: ');
|
|
225
|
-
setMessageColor('yellow');
|
|
226
|
-
setSuggestions([]);
|
|
227
|
-
setSelectedSuggestionIndex(-1);
|
|
228
|
-
setVisibleOffset(0);
|
|
492
|
+
dispatch({
|
|
493
|
+
type: 'RESET_WIZARD'
|
|
494
|
+
});
|
|
229
495
|
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Inserts the next pending suggested env var into envInput.
|
|
499
|
+
* Derives "next" by comparing keys already in envInput against suggestedEnv.
|
|
500
|
+
* When all suggestions are already present, sets a feedback message.
|
|
501
|
+
*/
|
|
502
|
+
function insertNextSuggestedEnv() {
|
|
503
|
+
var _profile$suggestedEnv;
|
|
504
|
+
var baseName = imageName.trim().toLowerCase().split(':')[0].split('/').pop();
|
|
505
|
+
var profile = imageProfiles[baseName];
|
|
506
|
+
var suggested = (_profile$suggestedEnv = profile === null || profile === void 0 ? void 0 : profile.suggestedEnv) !== null && _profile$suggestedEnv !== void 0 ? _profile$suggestedEnv : [];
|
|
507
|
+
if (!suggested.length) return;
|
|
508
|
+
var addedKeys = envInput.split(',').map(function (s) {
|
|
509
|
+
return s.split('=')[0].trim();
|
|
510
|
+
}).filter(Boolean);
|
|
511
|
+
var next = suggested.find(function (s) {
|
|
512
|
+
return !addedKeys.includes(s.split('=')[0]);
|
|
513
|
+
});
|
|
514
|
+
if (!next) {
|
|
515
|
+
setTimedMessage('All suggested env vars added', 'yellow');
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
var newEnvInput = envInput ? "".concat(envInput, ",").concat(next) : next;
|
|
519
|
+
dispatch({
|
|
520
|
+
type: 'SET',
|
|
521
|
+
payload: {
|
|
522
|
+
envInput: newEnvInput
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* True when the current image profile has at least one suggestedEnv entry.
|
|
529
|
+
*/
|
|
530
|
+
var baseName = imageName.trim().toLowerCase().split(':')[0].split('/').pop();
|
|
531
|
+
var hasSuggestedEnv = ((_imageProfiles$baseNa = (_imageProfiles$baseNa2 = imageProfiles[baseName]) === null || _imageProfiles$baseNa2 === void 0 || (_imageProfiles$baseNa2 = _imageProfiles$baseNa2.suggestedEnv) === null || _imageProfiles$baseNa2 === void 0 ? void 0 : _imageProfiles$baseNa2.length) !== null && _imageProfiles$baseNa !== void 0 ? _imageProfiles$baseNa : 0) > 0;
|
|
230
532
|
return {
|
|
231
533
|
step: step,
|
|
232
534
|
setStep: setStep,
|
|
@@ -245,11 +547,16 @@ export function useContainerCreation(_ref) {
|
|
|
245
547
|
suggestions: suggestions,
|
|
246
548
|
selectedSuggestionIndex: selectedSuggestionIndex,
|
|
247
549
|
visibleOffset: visibleOffset,
|
|
550
|
+
isSearchingHub: isSearchingHub,
|
|
551
|
+
hubResults: hubResults,
|
|
248
552
|
updateImageInput: updateImageInput,
|
|
553
|
+
triggerHubSearch: triggerHubSearch,
|
|
249
554
|
moveSuggestionSelection: moveSuggestionSelection,
|
|
250
555
|
applyFocusedSuggestion: applyFocusedSuggestion,
|
|
251
556
|
nextStep: nextStep,
|
|
252
557
|
cancelCreation: cancelCreation,
|
|
253
|
-
resetCreation: resetCreation
|
|
558
|
+
resetCreation: resetCreation,
|
|
559
|
+
insertNextSuggestedEnv: insertNextSuggestedEnv,
|
|
560
|
+
hasSuggestedEnv: hasSuggestedEnv
|
|
254
561
|
};
|
|
255
562
|
}
|