cdd-cli 3.2.0 → 4.0.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.
Files changed (136) hide show
  1. package/.atl/skill-registry.md +30 -0
  2. package/.editorconfig +12 -0
  3. package/.eslintrc.json +1 -1
  4. package/.prettierignore +4 -0
  5. package/.prettierrc +8 -0
  6. package/.~lock.ROADMAP.md# +1 -0
  7. package/CHANGELOG.md +80 -0
  8. package/README.es.md +198 -0
  9. package/README.md +81 -23
  10. package/ROADMAP.md +647 -0
  11. package/SECURITY.md +5 -0
  12. package/__mocks__/ink.cjs +17 -0
  13. package/coverage/clover.xml +471 -500
  14. package/coverage/coverage-final.json +16 -16
  15. package/coverage/lcov-report/ContainerCreationPrompt.jsx.html +400 -0
  16. package/coverage/lcov-report/ControlsHUD.jsx.html +256 -0
  17. package/coverage/lcov-report/components/ContainerCreationPrompt.jsx.html +394 -0
  18. package/coverage/lcov-report/components/PromptField.jsx.html +229 -0
  19. package/coverage/lcov-report/components/SuggestionPanel.jsx.html +268 -0
  20. package/coverage/lcov-report/components/index.html +146 -0
  21. package/coverage/lcov-report/helpers/constants.js.html +139 -22
  22. package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +75 -42
  23. package/coverage/lcov-report/helpers/dockerHubService.js.html +310 -0
  24. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +162 -84
  25. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +50 -32
  26. package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +21 -21
  27. package/coverage/lcov-report/helpers/imageNameUtils.js.html +181 -0
  28. package/coverage/lcov-report/helpers/index.html +84 -54
  29. package/coverage/lcov-report/helpers/logger.js.html +68 -68
  30. package/coverage/lcov-report/helpers/safeCall.js.html +15 -15
  31. package/coverage/lcov-report/helpers/validationHelpers.js.html +66 -114
  32. package/coverage/lcov-report/hooks/creation/index.html +20 -20
  33. package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +38 -17
  34. package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +593 -89
  35. package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +7 -7
  36. package/coverage/lcov-report/hooks/debug/index.html +5 -5
  37. package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +35 -20
  38. package/coverage/lcov-report/hooks/index.html +40 -40
  39. package/coverage/lcov-report/hooks/navigation/index.html +19 -19
  40. package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +38 -29
  41. package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +119 -80
  42. package/coverage/lcov-report/hooks/useControls.js.html +306 -123
  43. package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +8 -8
  44. package/coverage/lcov-report/hooks/useExitHandler.js.html +50 -41
  45. package/coverage/lcov-report/index.html +52 -67
  46. package/coverage/lcov.info +899 -952
  47. package/dist/App.js +18 -12
  48. package/dist/components/ContainerCreationPrompt.js +50 -13
  49. package/dist/components/ContainerList.js +3 -3
  50. package/dist/components/ContainerRow.js +20 -20
  51. package/dist/components/ContainerSection.js +3 -3
  52. package/dist/components/ControlsHUD.js +106 -0
  53. package/dist/components/Footer.js +2 -2
  54. package/dist/components/Header.js +7 -7
  55. package/dist/components/LogViewer.js +3 -3
  56. package/dist/components/MessageFeedback.js +2 -2
  57. package/dist/components/PromptField.js +4 -4
  58. package/dist/components/StatsBar.js +5 -5
  59. package/dist/components/SuggestionPanel.js +53 -0
  60. package/dist/components/UsageMenu.js +3 -3
  61. package/dist/helpers/appInfo.js +7 -7
  62. package/dist/helpers/constants.js +154 -1
  63. package/dist/helpers/containerOptionsBuilder.js +3 -3
  64. package/dist/helpers/dockerHubService.js +111 -0
  65. package/dist/helpers/dockerService/dockerService.js +1 -1
  66. package/dist/helpers/dockerService/serviceComponents/containerActions.js +99 -40
  67. package/dist/helpers/dockerService/serviceComponents/containerList.js +11 -11
  68. package/dist/helpers/dockerService/serviceComponents/containerLogs.js +9 -9
  69. package/dist/helpers/dockerService/serviceComponents/containerStats.js +5 -5
  70. package/dist/helpers/dockerService/serviceComponents/imageUtils.js +1 -1
  71. package/dist/helpers/exitWithMessage.js +10 -10
  72. package/dist/helpers/imageNameUtils.js +32 -0
  73. package/dist/helpers/logger.js +13 -13
  74. package/dist/helpers/safeCall.js +2 -2
  75. package/dist/helpers/validationHelpers.js +76 -21
  76. package/dist/hooks/creation/useContainerActions.js +28 -11
  77. package/dist/hooks/creation/useContainerCreation.js +452 -62
  78. package/dist/hooks/creation/useLogsViewer.js +2 -2
  79. package/dist/hooks/debug/useDebugLogs.js +4 -4
  80. package/dist/hooks/useContainerCommandRouter.js +14 -14
  81. package/dist/hooks/useContainers.js +3 -3
  82. package/dist/hooks/useControls.js +85 -38
  83. package/dist/hooks/useEraseConfirmation.js +3 -3
  84. package/dist/index.js +9 -2
  85. package/jest.config.cjs +7 -0
  86. package/package.json +7 -3
  87. package/src/App.jsx +39 -16
  88. package/src/components/ContainerCreationPrompt.jsx +54 -10
  89. package/src/components/ContainerList.jsx +3 -3
  90. package/src/components/ContainerRow.jsx +19 -16
  91. package/src/components/ContainerSection.jsx +3 -3
  92. package/src/components/ControlsHUD.jsx +66 -0
  93. package/src/components/Footer.jsx +2 -2
  94. package/src/components/Header.jsx +7 -7
  95. package/src/components/LogViewer.jsx +5 -3
  96. package/src/components/MessageFeedback.jsx +2 -2
  97. package/src/components/PromptField.jsx +4 -4
  98. package/src/components/StatsBar.jsx +11 -7
  99. package/src/components/SuggestionPanel.jsx +61 -0
  100. package/src/components/UsageMenu.jsx +18 -13
  101. package/src/helpers/appInfo.js +7 -7
  102. package/src/helpers/constants.js +48 -9
  103. package/src/helpers/containerOptionsBuilder.js +16 -5
  104. package/src/helpers/dockerHubService.js +75 -0
  105. package/src/helpers/dockerService/dockerService.js +1 -1
  106. package/src/helpers/dockerService/serviceComponents/containerActions.js +87 -61
  107. package/src/helpers/dockerService/serviceComponents/containerList.js +31 -25
  108. package/src/helpers/dockerService/serviceComponents/containerLogs.js +41 -26
  109. package/src/helpers/dockerService/serviceComponents/containerStats.js +13 -11
  110. package/src/helpers/dockerService/serviceComponents/imageUtils.js +5 -4
  111. package/src/helpers/exitWithMessage.js +13 -7
  112. package/src/helpers/imageNameUtils.js +32 -0
  113. package/src/helpers/logger.js +15 -16
  114. package/src/helpers/safeCall.js +2 -2
  115. package/src/helpers/validationHelpers.js +13 -29
  116. package/src/hooks/creation/useContainerActions.js +32 -12
  117. package/src/hooks/creation/useContainerCreation.js +293 -47
  118. package/src/hooks/creation/useLogsViewer.js +2 -2
  119. package/src/hooks/debug/useDebugLogs.js +15 -10
  120. package/src/hooks/navigation/useContainerSelection.js +17 -14
  121. package/src/hooks/useContainerCommandRouter.js +71 -58
  122. package/src/hooks/useContainers.js +4 -4
  123. package/src/hooks/useControls.js +135 -72
  124. package/src/hooks/useEraseConfirmation.js +3 -3
  125. package/src/hooks/useExitHandler.js +29 -26
  126. package/src/index.js +8 -4
  127. package/test/ContainerCreationPrompt.dom.test.js +313 -0
  128. package/test/ControlsHUD.dom.test.js +125 -0
  129. package/test/SuggestionPanel.dom.test.js +83 -0
  130. package/test/constants.test.js +41 -0
  131. package/test/dockerHubService.test.js +213 -0
  132. package/test/logger.test.js +58 -0
  133. package/test/resolveImageTag.test.js +26 -0
  134. package/test/useContainerCreation.dom.test.js +428 -2
  135. package/test/useContainerCreationHub.dom.test.js +184 -0
  136. package/test/useControls.dom.test.js +406 -4
@@ -44,9 +44,9 @@
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
- <span class="strong">74.07% </span>
47
+ <span class="strong">68.96% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>20/27</span>
49
+ <span class='fraction'>20/29</span>
50
50
  </div>
51
51
 
52
52
 
@@ -131,7 +131,13 @@
131
131
  <a name='L66'></a><a href='#L66'>66</a>
132
132
  <a name='L67'></a><a href='#L67'>67</a>
133
133
  <a name='L68'></a><a href='#L68'>68</a>
134
- <a name='L69'></a><a href='#L69'>69</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
134
+ <a name='L69'></a><a href='#L69'>69</a>
135
+ <a name='L70'></a><a href='#L70'>70</a>
136
+ <a name='L71'></a><a href='#L71'>71</a>
137
+ <a name='L72'></a><a href='#L72'>72</a>
138
+ <a name='L73'></a><a href='#L73'>73</a>
139
+ <a name='L74'></a><a href='#L74'>74</a>
140
+ <a name='L75'></a><a href='#L75'>75</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
135
141
  <span class="cline-any cline-neutral">&nbsp;</span>
136
142
  <span class="cline-any cline-neutral">&nbsp;</span>
137
143
  <span class="cline-any cline-yes">3x</span>
@@ -147,10 +153,17 @@
147
153
  <span class="cline-any cline-neutral">&nbsp;</span>
148
154
  <span class="cline-any cline-neutral">&nbsp;</span>
149
155
  <span class="cline-any cline-yes">3x</span>
156
+ <span class="cline-any cline-neutral">&nbsp;</span>
157
+ <span class="cline-any cline-neutral">&nbsp;</span>
158
+ <span class="cline-any cline-neutral">&nbsp;</span>
159
+ <span class="cline-any cline-neutral">&nbsp;</span>
150
160
  <span class="cline-any cline-yes">1x</span>
151
161
  <span class="cline-any cline-yes">2x</span>
152
162
  <span class="cline-any cline-no">&nbsp;</span>
153
163
  <span class="cline-any cline-yes">2x</span>
164
+ <span class="cline-any cline-neutral">&nbsp;</span>
165
+ <span class="cline-any cline-neutral">&nbsp;</span>
166
+ <span class="cline-any cline-neutral">&nbsp;</span>
154
167
  <span class="cline-any cline-yes">1x</span>
155
168
  <span class="cline-any cline-neutral">&nbsp;</span>
156
169
  <span class="cline-any cline-yes">1x</span>
@@ -180,17 +193,16 @@
180
193
  <span class="cline-any cline-neutral">&nbsp;</span>
181
194
  <span class="cline-any cline-neutral">&nbsp;</span>
182
195
  <span class="cline-any cline-neutral">&nbsp;</span>
183
- <span class="cline-any cline-neutral">&nbsp;</span>
184
196
  <span class="cline-any cline-yes">3x</span>
185
197
  <span class="cline-any cline-no">&nbsp;</span>
186
- <span class="cline-any cline-neutral">&nbsp;</span>
198
+ <span class="cline-any cline-no">&nbsp;</span>
187
199
  <span class="cline-any cline-yes">3x</span>
188
200
  <span class="cline-any cline-no">&nbsp;</span>
189
201
  <span class="cline-any cline-neutral">&nbsp;</span>
190
202
  <span class="cline-any cline-neutral">&nbsp;</span>
191
203
  <span class="cline-any cline-yes">3x</span>
192
204
  <span class="cline-any cline-no">&nbsp;</span>
193
- <span class="cline-any cline-neutral">&nbsp;</span>
205
+ <span class="cline-any cline-no">&nbsp;</span>
194
206
  <span class="cline-any cline-yes">3x</span>
195
207
  <span class="cline-any cline-neutral">&nbsp;</span>
196
208
  <span class="cline-any cline-neutral">&nbsp;</span>
@@ -199,10 +211,10 @@
199
211
  <span class="cline-any cline-no">&nbsp;</span>
200
212
  <span class="cline-any cline-neutral">&nbsp;</span>
201
213
  <span class="cline-any cline-neutral">&nbsp;</span>
202
- <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { docker } from "../dockerService.js";
203
- import { logger } from "../../logger.js";
214
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { docker } from '../dockerService.js';
215
+ import { logger } from '../../logger.js';
204
216
  &nbsp;
205
- const DEFAULT_CONTAINER_NAME = "Unknown";
217
+ const DEFAULT_CONTAINER_NAME = 'Unknown';
206
218
  &nbsp;
207
219
  /**
208
220
  * Produce a safe, display-friendly container name from the raw Docker payload.
@@ -214,21 +226,28 @@ function normalizeContainerName(container) {
214
226
  const names = container?.Names;
215
227
  &nbsp;
216
228
  let rawName;
217
- if (Array.isArray(names) &amp;&amp; names.length &gt; 0 &amp;&amp; typeof names[0] === "string") {
229
+ if (
230
+ Array.isArray(names) &amp;&amp;
231
+ names.length &gt; 0 &amp;&amp;
232
+ typeof names[0] === 'string'
233
+ ) {
218
234
  rawName = names[0];
219
- } else <span class="missing-if-branch" title="if path not taken" >I</span>if (typeof names === "string" &amp;&amp; <span class="branch-1 cbranch-no" title="branch not covered" >names.trim().length &gt; 0)</span> {
235
+ } else <span class="missing-if-branch" title="if path not taken" >I</span>if (typeof names === 'string' &amp;&amp; <span class="branch-1 cbranch-no" title="branch not covered" >names.trim().length &gt; 0)</span> {
220
236
  <span class="cstat-no" title="statement not covered" > rawName = names;</span>
221
- } else if (typeof container?.Name === "string" &amp;&amp; container.Name.trim().length &gt; 0) {
237
+ } else if (
238
+ typeof container?.Name === 'string' &amp;&amp;
239
+ container.Name.trim().length &gt; 0
240
+ ) {
222
241
  rawName = container.Name;
223
242
  } else {
224
243
  rawName = DEFAULT_CONTAINER_NAME;
225
244
  }
226
245
  &nbsp;
227
- <span class="missing-if-branch" title="if path not taken" >I</span>if (typeof rawName !== "string") {
246
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (typeof rawName !== 'string') {
228
247
  <span class="cstat-no" title="statement not covered" > return DEFAULT_CONTAINER_NAME;</span>
229
248
  }
230
249
  &nbsp;
231
- const cleanedName = rawName.replace(/^\/+/u, "").trim();
250
+ const cleanedName = rawName.replace(/^\/+/u, '').trim();
232
251
  return cleanedName.length &gt; 0 ? cleanedName : <span class="branch-1 cbranch-no" title="branch not covered" >DEFAULT_CONTAINER_NAME;</span>
233
252
  }
234
253
  &nbsp;
@@ -237,33 +256,32 @@ function normalizeContainerName(container) {
237
256
  * @returns {Promise&lt;Array&lt;Object&gt;&gt;}
238
257
  */
239
258
  export async function getContainers() {
240
- logger.debug("Listing containers");
259
+ logger.debug('Listing containers');
241
260
  try {
242
261
  const containers = await docker.listContainers({ all: true });
243
- logger.debug("Retrieved %d containers", containers.length);
262
+ logger.debug('Retrieved %d containers', containers.length);
244
263
  return containers.map((container) =&gt; ({
245
264
  id: container.Id,
246
265
  name: normalizeContainerName(container),
247
266
  image: container.Image,
248
267
  state: container.State,
249
268
  status: container.Status,
250
- ports:
251
- (() =&gt; {
252
- const publicPorts = container.Ports.filter(<span class="fstat-no" title="function not covered" >(p</span>ort) =&gt; <span class="cstat-no" title="statement not covered" >port.PublicPort)</span>.map(
253
- <span class="fstat-no" title="function not covered" > (p</span>ort) =&gt; <span class="cstat-no" title="statement not covered" >`${port.PublicPort}:${port.PrivatePort}`</span>
254
- );
255
- <span class="missing-if-branch" title="if path not taken" >I</span>if (publicPorts.length &gt; 0) {
256
- <span class="cstat-no" title="statement not covered" > return [...new Set(publicPorts)];</span>
257
- }
258
- // Fall back to exposed private ports when there are no bindings.
259
- const privatePorts = container.Ports.filter(<span class="fstat-no" title="function not covered" >(p</span>ort) =&gt; <span class="cstat-no" title="statement not covered" >port.PrivatePort)</span>.map(
260
- <span class="fstat-no" title="function not covered" > (p</span>ort) =&gt; <span class="cstat-no" title="statement not covered" >`${port.PrivatePort}`</span>
261
- );
262
- return [...new Set(privatePorts)];
263
- })()
269
+ ports: (() =&gt; {
270
+ const publicPorts = container.Ports.filter(
271
+ <span class="fstat-no" title="function not covered" > (p</span>ort) =&gt; <span class="cstat-no" title="statement not covered" >port.PublicPort</span>
272
+ ).map(<span class="fstat-no" title="function not covered" >(p</span>ort) =&gt; <span class="cstat-no" title="statement not covered" >`${port.PublicPort}:${port.PrivatePort}`)</span>;
273
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (publicPorts.length &gt; 0) {
274
+ <span class="cstat-no" title="statement not covered" > return [...new Set(publicPorts)];</span>
275
+ }
276
+ // Fall back to exposed private ports when there are no bindings.
277
+ const privatePorts = container.Ports.filter(
278
+ <span class="fstat-no" title="function not covered" > (p</span>ort) =&gt; <span class="cstat-no" title="statement not covered" >port.PrivatePort</span>
279
+ ).map(<span class="fstat-no" title="function not covered" >(p</span>ort) =&gt; <span class="cstat-no" title="statement not covered" >`${port.PrivatePort}`)</span>;
280
+ return [...new Set(privatePorts)];
281
+ })(),
264
282
  }));
265
283
  } catch (err) {
266
- <span class="cstat-no" title="statement not covered" > logger.error("Failed to list containers", err);</span>
284
+ <span class="cstat-no" title="statement not covered" > logger.error('Failed to list containers', err);</span>
267
285
  <span class="cstat-no" title="statement not covered" > throw err;</span>
268
286
  }
269
287
  }
@@ -274,7 +292,7 @@ export async function getContainers() {
274
292
  <div class='footer quiet pad2 space-top1 center small'>
275
293
  Code coverage generated by
276
294
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
277
- at 2026-04-25T17:40:28.828Z
295
+ at 2026-04-27T21:36:40.677Z
278
296
  </div>
279
297
  <script src="../../../prettify.js"></script>
280
298
  <script>
@@ -23,30 +23,30 @@
23
23
  <div class='clearfix'>
24
24
 
25
25
  <div class='fl pad1y space-right2'>
26
- <span class="strong">71.69% </span>
26
+ <span class="strong">71.14% </span>
27
27
  <span class="quiet">Statements</span>
28
- <span class='fraction'>114/159</span>
28
+ <span class='fraction'>106/149</span>
29
29
  </div>
30
30
 
31
31
 
32
32
  <div class='fl pad1y space-right2'>
33
- <span class="strong">63.21% </span>
33
+ <span class="strong">64.19% </span>
34
34
  <span class="quiet">Branches</span>
35
- <span class='fraction'>55/87</span>
35
+ <span class='fraction'>52/81</span>
36
36
  </div>
37
37
 
38
38
 
39
39
  <div class='fl pad1y space-right2'>
40
- <span class="strong">70.83% </span>
40
+ <span class="strong">69.56% </span>
41
41
  <span class="quiet">Functions</span>
42
- <span class='fraction'>17/24</span>
42
+ <span class='fraction'>16/23</span>
43
43
  </div>
44
44
 
45
45
 
46
46
  <div class='fl pad1y space-right2'>
47
- <span class="strong">73.68% </span>
47
+ <span class="strong">71.42% </span>
48
48
  <span class="quiet">Lines</span>
49
- <span class='fraction'>112/152</span>
49
+ <span class='fraction'>105/147</span>
50
50
  </div>
51
51
 
52
52
 
@@ -80,17 +80,17 @@
80
80
  </thead>
81
81
  <tbody><tr>
82
82
  <td class="file medium" data-value="containerActions.js"><a href="containerActions.js.html">containerActions.js</a></td>
83
- <td data-value="72.09" class="pic medium">
84
- <div class="chart"><div class="cover-fill" style="width: 72%"></div><div class="cover-empty" style="width: 28%"></div></div>
83
+ <td data-value="71.42" class="pic medium">
84
+ <div class="chart"><div class="cover-fill" style="width: 71%"></div><div class="cover-empty" style="width: 29%"></div></div>
85
85
  </td>
86
- <td data-value="72.09" class="pct medium">72.09%</td>
87
- <td data-value="129" class="abs medium">93/129</td>
88
- <td data-value="60.29" class="pct medium">60.29%</td>
89
- <td data-value="68" class="abs medium">41/68</td>
90
- <td data-value="81.25" class="pct high">81.25%</td>
91
- <td data-value="16" class="abs high">13/16</td>
92
- <td data-value="73.6" class="pct medium">73.6%</td>
93
- <td data-value="125" class="abs medium">92/125</td>
86
+ <td data-value="71.42" class="pct medium">71.42%</td>
87
+ <td data-value="119" class="abs medium">85/119</td>
88
+ <td data-value="61.29" class="pct medium">61.29%</td>
89
+ <td data-value="62" class="abs medium">38/62</td>
90
+ <td data-value="80" class="pct high">80%</td>
91
+ <td data-value="15" class="abs high">12/15</td>
92
+ <td data-value="72.03" class="pct medium">72.03%</td>
93
+ <td data-value="118" class="abs medium">85/118</td>
94
94
  </tr>
95
95
 
96
96
  <tr>
@@ -104,8 +104,8 @@
104
104
  <td data-value="19" class="abs medium">14/19</td>
105
105
  <td data-value="50" class="pct medium">50%</td>
106
106
  <td data-value="8" class="abs medium">4/8</td>
107
- <td data-value="74.07" class="pct medium">74.07%</td>
108
- <td data-value="27" class="abs medium">20/27</td>
107
+ <td data-value="68.96" class="pct medium">68.96%</td>
108
+ <td data-value="29" class="abs medium">20/29</td>
109
109
  </tr>
110
110
 
111
111
  </tbody>
@@ -116,7 +116,7 @@
116
116
  <div class='footer quiet pad2 space-top1 center small'>
117
117
  Code coverage generated by
118
118
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
119
- at 2026-04-25T17:40:28.828Z
119
+ at 2026-04-27T21:36:40.677Z
120
120
  </div>
121
121
  <script src="../../../prettify.js"></script>
122
122
  <script>
@@ -0,0 +1,181 @@
1
+
2
+ <!doctype html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <title>Code coverage report for helpers/imageNameUtils.js</title>
7
+ <meta charset="utf-8" />
8
+ <link rel="stylesheet" href="../prettify.css" />
9
+ <link rel="stylesheet" href="../base.css" />
10
+ <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
12
+ <style type='text/css'>
13
+ .coverage-summary .sorter {
14
+ background-image: url(../sort-arrow-sprite.png);
15
+ }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <div class='wrapper'>
21
+ <div class='pad1'>
22
+ <h1><a href="../index.html">All files</a> / <a href="index.html">helpers</a> imageNameUtils.js</h1>
23
+ <div class='clearfix'>
24
+
25
+ <div class='fl pad1y space-right2'>
26
+ <span class="strong">80% </span>
27
+ <span class="quiet">Statements</span>
28
+ <span class='fraction'>8/10</span>
29
+ </div>
30
+
31
+
32
+ <div class='fl pad1y space-right2'>
33
+ <span class="strong">66.66% </span>
34
+ <span class="quiet">Branches</span>
35
+ <span class='fraction'>4/6</span>
36
+ </div>
37
+
38
+
39
+ <div class='fl pad1y space-right2'>
40
+ <span class="strong">100% </span>
41
+ <span class="quiet">Functions</span>
42
+ <span class='fraction'>1/1</span>
43
+ </div>
44
+
45
+
46
+ <div class='fl pad1y space-right2'>
47
+ <span class="strong">88.88% </span>
48
+ <span class="quiet">Lines</span>
49
+ <span class='fraction'>8/9</span>
50
+ </div>
51
+
52
+
53
+ </div>
54
+ <p class="quiet">
55
+ Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
56
+ </p>
57
+ <template id="filterTemplate">
58
+ <div class="quiet">
59
+ Filter:
60
+ <input type="search" id="fileSearch">
61
+ </div>
62
+ </template>
63
+ </div>
64
+ <div class='status-line high'></div>
65
+ <pre><table class="coverage">
66
+ <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
67
+ <a name='L2'></a><a href='#L2'>2</a>
68
+ <a name='L3'></a><a href='#L3'>3</a>
69
+ <a name='L4'></a><a href='#L4'>4</a>
70
+ <a name='L5'></a><a href='#L5'>5</a>
71
+ <a name='L6'></a><a href='#L6'>6</a>
72
+ <a name='L7'></a><a href='#L7'>7</a>
73
+ <a name='L8'></a><a href='#L8'>8</a>
74
+ <a name='L9'></a><a href='#L9'>9</a>
75
+ <a name='L10'></a><a href='#L10'>10</a>
76
+ <a name='L11'></a><a href='#L11'>11</a>
77
+ <a name='L12'></a><a href='#L12'>12</a>
78
+ <a name='L13'></a><a href='#L13'>13</a>
79
+ <a name='L14'></a><a href='#L14'>14</a>
80
+ <a name='L15'></a><a href='#L15'>15</a>
81
+ <a name='L16'></a><a href='#L16'>16</a>
82
+ <a name='L17'></a><a href='#L17'>17</a>
83
+ <a name='L18'></a><a href='#L18'>18</a>
84
+ <a name='L19'></a><a href='#L19'>19</a>
85
+ <a name='L20'></a><a href='#L20'>20</a>
86
+ <a name='L21'></a><a href='#L21'>21</a>
87
+ <a name='L22'></a><a href='#L22'>22</a>
88
+ <a name='L23'></a><a href='#L23'>23</a>
89
+ <a name='L24'></a><a href='#L24'>24</a>
90
+ <a name='L25'></a><a href='#L25'>25</a>
91
+ <a name='L26'></a><a href='#L26'>26</a>
92
+ <a name='L27'></a><a href='#L27'>27</a>
93
+ <a name='L28'></a><a href='#L28'>28</a>
94
+ <a name='L29'></a><a href='#L29'>29</a>
95
+ <a name='L30'></a><a href='#L30'>30</a>
96
+ <a name='L31'></a><a href='#L31'>31</a>
97
+ <a name='L32'></a><a href='#L32'>32</a>
98
+ <a name='L33'></a><a href='#L33'>33</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
99
+ <span class="cline-any cline-neutral">&nbsp;</span>
100
+ <span class="cline-any cline-neutral">&nbsp;</span>
101
+ <span class="cline-any cline-neutral">&nbsp;</span>
102
+ <span class="cline-any cline-neutral">&nbsp;</span>
103
+ <span class="cline-any cline-neutral">&nbsp;</span>
104
+ <span class="cline-any cline-neutral">&nbsp;</span>
105
+ <span class="cline-any cline-neutral">&nbsp;</span>
106
+ <span class="cline-any cline-neutral">&nbsp;</span>
107
+ <span class="cline-any cline-neutral">&nbsp;</span>
108
+ <span class="cline-any cline-neutral">&nbsp;</span>
109
+ <span class="cline-any cline-neutral">&nbsp;</span>
110
+ <span class="cline-any cline-neutral">&nbsp;</span>
111
+ <span class="cline-any cline-neutral">&nbsp;</span>
112
+ <span class="cline-any cline-neutral">&nbsp;</span>
113
+ <span class="cline-any cline-neutral">&nbsp;</span>
114
+ <span class="cline-any cline-neutral">&nbsp;</span>
115
+ <span class="cline-any cline-neutral">&nbsp;</span>
116
+ <span class="cline-any cline-yes">7x</span>
117
+ <span class="cline-any cline-neutral">&nbsp;</span>
118
+ <span class="cline-any cline-yes">7x</span>
119
+ <span class="cline-any cline-yes">7x</span>
120
+ <span class="cline-any cline-yes">7x</span>
121
+ <span class="cline-any cline-no">&nbsp;</span>
122
+ <span class="cline-any cline-neutral">&nbsp;</span>
123
+ <span class="cline-any cline-neutral">&nbsp;</span>
124
+ <span class="cline-any cline-yes">7x</span>
125
+ <span class="cline-any cline-yes">7x</span>
126
+ <span class="cline-any cline-yes">5x</span>
127
+ <span class="cline-any cline-neutral">&nbsp;</span>
128
+ <span class="cline-any cline-yes">7x</span>
129
+ <span class="cline-any cline-neutral">&nbsp;</span>
130
+ <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">/**
131
+ * Utility for normalizing Docker image names.
132
+ * Extracted from validationHelpers.js and containerActions.js — both had
133
+ * identical implementations; consolidated here to avoid duplication.
134
+ */
135
+ &nbsp;
136
+ /**
137
+ * Normalize an image name to its base name (strips registry prefix and tag).
138
+ *
139
+ * Examples:
140
+ * "docker.io/library/postgres:16-alpine" → "postgres"
141
+ * "nginx:alpine" → "nginx"
142
+ * "myregistry.io/myorg/myapp:latest" → "myapp"
143
+ *
144
+ * @param {string} imageName - Raw image name (from user input or Docker API)
145
+ * @returns {string} Base image name in lowercase, empty string if input is falsy
146
+ */
147
+ export function normalizeImageName(imageName) {
148
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!imageName) <span class="cstat-no" title="statement not covered" >return '';</span>
149
+ // Remove registry prefix (everything up to and including the last '/')
150
+ let name = imageName;
151
+ const slashIdx = name.lastIndexOf('/');
152
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (slashIdx !== -1) {
153
+ <span class="cstat-no" title="statement not covered" > name = name.slice(slashIdx + 1);</span>
154
+ }
155
+ // Remove tag (everything from the first ':' onward)
156
+ const colonIdx = name.indexOf(':');
157
+ if (colonIdx !== -1) {
158
+ name = name.slice(0, colonIdx);
159
+ }
160
+ return name.toLowerCase();
161
+ }
162
+ &nbsp;</pre></td></tr></table></pre>
163
+
164
+ <div class='push'></div><!-- for sticky footer -->
165
+ </div><!-- /wrapper -->
166
+ <div class='footer quiet pad2 space-top1 center small'>
167
+ Code coverage generated by
168
+ <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
169
+ at 2026-04-27T22:13:21.150Z
170
+ </div>
171
+ <script src="../prettify.js"></script>
172
+ <script>
173
+ window.onload = function () {
174
+ prettyPrint();
175
+ };
176
+ </script>
177
+ <script src="../sorter.js"></script>
178
+ <script src="../block-navigation.js"></script>
179
+ </body>
180
+ </html>
181
+