infra-kit 0.1.126 → 0.1.128

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 (335) hide show
  1. package/.eslintcache +1 -1
  2. package/.omc/state/agent-replay-036daa78-5fee-4c7d-9b44-53584acb0d87.jsonl +26 -0
  3. package/.omc/state/agent-replay-0a65ec83-9de7-458d-9d0f-e56e1a26c68e.jsonl +2 -0
  4. package/.omc/state/agent-replay-1b6ffc57-2d9b-4af4-b603-f1cef7e01b0a.jsonl +17 -0
  5. package/.omc/state/agent-replay-2ac0dcd0-01f7-41a8-a8c9-f2195599817b.jsonl +5 -0
  6. package/.omc/state/agent-replay-d02e6565-5168-40c8-8629-de219a29fa43.jsonl +2 -0
  7. package/.omc/state/idle-notif-cooldown.json +1 -1
  8. package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/last-tool-error-state.json +7 -0
  9. package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/mission-state.json +129 -0
  10. package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/pre-tool-advisory-throttle.json +34 -0
  11. package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/ralph-state.json +13 -0
  12. package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/ralplan-state.json +11 -0
  13. package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/subagent-tracking-state.json +80 -0
  14. package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/ultrawork-state.json +11 -0
  15. package/.omc/state/sessions/0a65ec83-9de7-458d-9d0f-e56e1a26c68e/pre-tool-advisory-throttle.json +18 -0
  16. package/.omc/state/sessions/0a65ec83-9de7-458d-9d0f-e56e1a26c68e/skill-active-state.json +15 -0
  17. package/.omc/state/sessions/1b6ffc57-2d9b-4af4-b603-f1cef7e01b0a/last-tool-error-state.json +7 -0
  18. package/.omc/state/sessions/1b6ffc57-2d9b-4af4-b603-f1cef7e01b0a/mission-state.json +139 -0
  19. package/.omc/state/sessions/1b6ffc57-2d9b-4af4-b603-f1cef7e01b0a/pre-tool-advisory-throttle.json +50 -0
  20. package/.omc/state/sessions/1b6ffc57-2d9b-4af4-b603-f1cef7e01b0a/ralplan-state.json +11 -0
  21. package/.omc/state/sessions/1b6ffc57-2d9b-4af4-b603-f1cef7e01b0a/subagent-tracking-state.json +90 -0
  22. package/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/last-tool-error-state.json +7 -0
  23. package/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/mission-state.json +79 -0
  24. package/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/pre-tool-advisory-throttle.json +10 -0
  25. package/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/skill-active-state.json +15 -0
  26. package/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/subagent-tracking-state.json +26 -0
  27. package/.omc/state/sessions/7772f972-4ec1-4287-bf42-a92c14cf3ffc/pre-tool-advisory-throttle.json +10 -0
  28. package/.omc/state/sessions/d02e6565-5168-40c8-8629-de219a29fa43/last-tool-error-state.json +7 -0
  29. package/.omc/state/sessions/d02e6565-5168-40c8-8629-de219a29fa43/mission-state.json +53 -0
  30. package/.omc/state/sessions/d02e6565-5168-40c8-8629-de219a29fa43/pre-tool-advisory-throttle.json +18 -0
  31. package/.omc/state/sessions/d02e6565-5168-40c8-8629-de219a29fa43/subagent-tracking-state.json +17 -0
  32. package/.omc/state/sessions/f9c17a0e-2dd8-4f7d-badf-631f266c5d4c/pre-tool-advisory-throttle.json +10 -0
  33. package/.turbo/turbo-build.log +7 -6
  34. package/.turbo/turbo-check.log +1 -1
  35. package/.turbo/turbo-eslint-check.log +2 -1
  36. package/.turbo/turbo-infra-kit-check.log +14 -0
  37. package/.turbo/turbo-prettier-check.log +1 -1
  38. package/.turbo/turbo-test.log +406 -128
  39. package/.turbo/turbo-ts-check.log +1 -1
  40. package/coverage/base.css +224 -0
  41. package/coverage/block-navigation.js +87 -0
  42. package/coverage/cli/index.html +116 -0
  43. package/coverage/cli/package.json.html +283 -0
  44. package/coverage/cli/src/commands/audit/audit.ts.html +616 -0
  45. package/coverage/cli/src/commands/audit/index.html +131 -0
  46. package/coverage/cli/src/commands/audit/index.ts.html +88 -0
  47. package/coverage/cli/src/commands/doctor/doctor.ts.html +1654 -0
  48. package/coverage/cli/src/commands/doctor/index.html +131 -0
  49. package/coverage/cli/src/commands/doctor/index.ts.html +88 -0
  50. package/coverage/cli/src/commands/env-clear/env-clear.ts.html +370 -0
  51. package/coverage/cli/src/commands/env-clear/index.html +131 -0
  52. package/coverage/cli/src/commands/env-clear/index.ts.html +88 -0
  53. package/coverage/cli/src/commands/env-list/env-list.ts.html +229 -0
  54. package/coverage/cli/src/commands/env-list/index.html +131 -0
  55. package/coverage/cli/src/commands/env-list/index.ts.html +88 -0
  56. package/coverage/cli/src/commands/env-load/env-load.ts.html +1201 -0
  57. package/coverage/cli/src/commands/env-load/index.html +131 -0
  58. package/coverage/cli/src/commands/env-load/index.ts.html +91 -0
  59. package/coverage/cli/src/commands/env-status/env-status.ts.html +409 -0
  60. package/coverage/cli/src/commands/env-status/index.html +131 -0
  61. package/coverage/cli/src/commands/env-status/index.ts.html +88 -0
  62. package/coverage/cli/src/commands/gh-merge-dev/gh-merge-dev.ts.html +694 -0
  63. package/coverage/cli/src/commands/gh-merge-dev/index.html +131 -0
  64. package/coverage/cli/src/commands/gh-merge-dev/index.ts.html +88 -0
  65. package/coverage/cli/src/commands/gh-release-deliver/gh-release-deliver.ts.html +1495 -0
  66. package/coverage/cli/src/commands/gh-release-deliver/index.html +131 -0
  67. package/coverage/cli/src/commands/gh-release-deliver/index.ts.html +88 -0
  68. package/coverage/cli/src/commands/gh-release-deploy-all/gh-release-deploy-all.ts.html +706 -0
  69. package/coverage/cli/src/commands/gh-release-deploy-all/index.html +131 -0
  70. package/coverage/cli/src/commands/gh-release-deploy-all/index.ts.html +88 -0
  71. package/coverage/cli/src/commands/gh-release-deploy-selected/gh-release-deploy-selected.ts.html +994 -0
  72. package/coverage/cli/src/commands/gh-release-deploy-selected/index.html +131 -0
  73. package/coverage/cli/src/commands/gh-release-deploy-selected/index.ts.html +88 -0
  74. package/coverage/cli/src/commands/gh-release-list/gh-release-list.ts.html +355 -0
  75. package/coverage/cli/src/commands/gh-release-list/index.html +131 -0
  76. package/coverage/cli/src/commands/gh-release-list/index.ts.html +88 -0
  77. package/coverage/cli/src/commands/init/agent-files.ts.html +742 -0
  78. package/coverage/cli/src/commands/init/index.html +146 -0
  79. package/coverage/cli/src/commands/init/init.ts.html +1159 -0
  80. package/coverage/cli/src/commands/init/migrate-config.ts.html +1219 -0
  81. package/coverage/cli/src/commands/release-create/index.html +131 -0
  82. package/coverage/cli/src/commands/release-create/index.ts.html +88 -0
  83. package/coverage/cli/src/commands/release-create/release-create.ts.html +1426 -0
  84. package/coverage/cli/src/commands/release-desc-edit/index.html +131 -0
  85. package/coverage/cli/src/commands/release-desc-edit/index.ts.html +88 -0
  86. package/coverage/cli/src/commands/release-desc-edit/release-desc-edit.ts.html +766 -0
  87. package/coverage/cli/src/commands/vendor-check/index.html +131 -0
  88. package/coverage/cli/src/commands/vendor-check/index.ts.html +88 -0
  89. package/coverage/cli/src/commands/vendor-check/vendor-check.ts.html +466 -0
  90. package/coverage/cli/src/commands/vendor-config/index.html +116 -0
  91. package/coverage/cli/src/commands/vendor-config/vendor-config.ts.html +571 -0
  92. package/coverage/cli/src/commands/vendor-diff/index.html +131 -0
  93. package/coverage/cli/src/commands/vendor-diff/index.ts.html +88 -0
  94. package/coverage/cli/src/commands/vendor-diff/vendor-diff.ts.html +316 -0
  95. package/coverage/cli/src/commands/vendor-manifest/index.html +131 -0
  96. package/coverage/cli/src/commands/vendor-manifest/index.ts.html +88 -0
  97. package/coverage/cli/src/commands/vendor-manifest/vendor-manifest.ts.html +265 -0
  98. package/coverage/cli/src/commands/vendor-sync/index.html +131 -0
  99. package/coverage/cli/src/commands/vendor-sync/index.ts.html +88 -0
  100. package/coverage/cli/src/commands/vendor-sync/vendor-sync.ts.html +355 -0
  101. package/coverage/cli/src/commands/version/index.html +131 -0
  102. package/coverage/cli/src/commands/version/index.ts.html +88 -0
  103. package/coverage/cli/src/commands/version/version.ts.html +184 -0
  104. package/coverage/cli/src/commands/worktrees-add/index.html +131 -0
  105. package/coverage/cli/src/commands/worktrees-add/index.ts.html +88 -0
  106. package/coverage/cli/src/commands/worktrees-add/worktrees-add.ts.html +1183 -0
  107. package/coverage/cli/src/commands/worktrees-list/index.html +131 -0
  108. package/coverage/cli/src/commands/worktrees-list/index.ts.html +88 -0
  109. package/coverage/cli/src/commands/worktrees-list/worktrees-list.ts.html +400 -0
  110. package/coverage/cli/src/commands/worktrees-reload/index.html +131 -0
  111. package/coverage/cli/src/commands/worktrees-reload/index.ts.html +88 -0
  112. package/coverage/cli/src/commands/worktrees-reload/worktrees-reload.ts.html +775 -0
  113. package/coverage/cli/src/commands/worktrees-remove/index.html +131 -0
  114. package/coverage/cli/src/commands/worktrees-remove/index.ts.html +88 -0
  115. package/coverage/cli/src/commands/worktrees-remove/worktrees-remove.ts.html +682 -0
  116. package/coverage/cli/src/commands/worktrees-sync/index.html +131 -0
  117. package/coverage/cli/src/commands/worktrees-sync/index.ts.html +88 -0
  118. package/coverage/cli/src/commands/worktrees-sync/worktrees-sync.ts.html +679 -0
  119. package/coverage/cli/src/dev/__tests__/fixtures.ts.html +787 -0
  120. package/coverage/cli/src/dev/__tests__/index.html +116 -0
  121. package/coverage/cli/src/dev/dev-server.ts.html +1855 -0
  122. package/coverage/cli/src/dev/discovery.ts.html +598 -0
  123. package/coverage/cli/src/dev/index.html +191 -0
  124. package/coverage/cli/src/dev/interfaces.ts.html +223 -0
  125. package/coverage/cli/src/dev/logger.ts.html +1102 -0
  126. package/coverage/cli/src/dev/ports.ts.html +430 -0
  127. package/coverage/cli/src/dev/serverless-local-run.ts.html +916 -0
  128. package/coverage/cli/src/entry/dev-server.ts.html +409 -0
  129. package/coverage/cli/src/entry/index.html +116 -0
  130. package/coverage/cli/src/index.html +116 -0
  131. package/coverage/cli/src/integrations/cmux/canonicalize-cmux-title.ts.html +178 -0
  132. package/coverage/cli/src/integrations/cmux/close-workspace-by-title.ts.html +262 -0
  133. package/coverage/cli/src/integrations/cmux/index.html +191 -0
  134. package/coverage/cli/src/integrations/cmux/index.ts.html +100 -0
  135. package/coverage/cli/src/integrations/cmux/list-workspace-titles.ts.html +226 -0
  136. package/coverage/cli/src/integrations/cmux/open-workspace-with-layout.ts.html +325 -0
  137. package/coverage/cli/src/integrations/cmux/workspace-title.ts.html +154 -0
  138. package/coverage/cli/src/integrations/cursor/add-folders-to-workspace.ts.html +337 -0
  139. package/coverage/cli/src/integrations/cursor/index.html +206 -0
  140. package/coverage/cli/src/integrations/cursor/index.ts.html +103 -0
  141. package/coverage/cli/src/integrations/cursor/launch-cursor.ts.html +121 -0
  142. package/coverage/cli/src/integrations/cursor/open-cursor-workspace.ts.html +277 -0
  143. package/coverage/cli/src/integrations/cursor/reconcile-workspace-folders.ts.html +355 -0
  144. package/coverage/cli/src/integrations/cursor/remove-folders-from-workspace.ts.html +364 -0
  145. package/coverage/cli/src/integrations/cursor/resolve-workspace-path.ts.html +124 -0
  146. package/coverage/cli/src/integrations/doppler/doppler-cli-auth.ts.html +178 -0
  147. package/coverage/cli/src/integrations/doppler/doppler-errors.ts.html +349 -0
  148. package/coverage/cli/src/integrations/doppler/doppler-project.ts.html +355 -0
  149. package/coverage/cli/src/integrations/doppler/index.html +161 -0
  150. package/coverage/cli/src/integrations/doppler/index.ts.html +97 -0
  151. package/coverage/cli/src/integrations/gh/gh-cli-auth/gh-cli-auth.ts.html +172 -0
  152. package/coverage/cli/src/integrations/gh/gh-cli-auth/index.html +131 -0
  153. package/coverage/cli/src/integrations/gh/gh-cli-auth/index.ts.html +88 -0
  154. package/coverage/cli/src/integrations/gh/gh-release-prs/gh-release-prs.ts.html +703 -0
  155. package/coverage/cli/src/integrations/gh/gh-release-prs/index.html +131 -0
  156. package/coverage/cli/src/integrations/gh/gh-release-prs/index.ts.html +91 -0
  157. package/coverage/cli/src/integrations/gh/index.html +116 -0
  158. package/coverage/cli/src/integrations/gh/index.ts.html +94 -0
  159. package/coverage/cli/src/integrations/ide/add-ide-worktree-folders.ts.html +316 -0
  160. package/coverage/cli/src/integrations/ide/index.html +191 -0
  161. package/coverage/cli/src/integrations/ide/index.ts.html +103 -0
  162. package/coverage/cli/src/integrations/ide/open-ide-workspace.ts.html +238 -0
  163. package/coverage/cli/src/integrations/ide/provider-label.ts.html +157 -0
  164. package/coverage/cli/src/integrations/ide/remove-ide-worktree-folders.ts.html +520 -0
  165. package/coverage/cli/src/integrations/ide/types.ts.html +208 -0
  166. package/coverage/cli/src/integrations/jira/api.ts.html +1060 -0
  167. package/coverage/cli/src/integrations/jira/index.html +131 -0
  168. package/coverage/cli/src/integrations/jira/index.ts.html +142 -0
  169. package/coverage/cli/src/integrations/zed/add-folders-to-zed-workspace.ts.html +175 -0
  170. package/coverage/cli/src/integrations/zed/index.html +161 -0
  171. package/coverage/cli/src/integrations/zed/index.ts.html +94 -0
  172. package/coverage/cli/src/integrations/zed/open-zed-workspace.ts.html +238 -0
  173. package/coverage/cli/src/integrations/zed/reuse-zed-workspace.ts.html +256 -0
  174. package/coverage/cli/src/lib/assert-never/assert-never.ts.html +130 -0
  175. package/coverage/cli/src/lib/assert-never/index.html +131 -0
  176. package/coverage/cli/src/lib/assert-never/index.ts.html +88 -0
  177. package/coverage/cli/src/lib/command-catalog/command-catalog.ts.html +523 -0
  178. package/coverage/cli/src/lib/command-catalog/index.html +116 -0
  179. package/coverage/cli/src/lib/command-echo/command-echo.ts.html +319 -0
  180. package/coverage/cli/src/lib/command-echo/index.html +131 -0
  181. package/coverage/cli/src/lib/command-echo/index.ts.html +88 -0
  182. package/coverage/cli/src/lib/constants/constants.ts.html +472 -0
  183. package/coverage/cli/src/lib/constants/index.html +131 -0
  184. package/coverage/cli/src/lib/constants/index.ts.html +139 -0
  185. package/coverage/cli/src/lib/env-autoload/env-autoload.ts.html +1111 -0
  186. package/coverage/cli/src/lib/env-autoload/index.html +116 -0
  187. package/coverage/cli/src/lib/errors/format-zx-error.ts.html +247 -0
  188. package/coverage/cli/src/lib/errors/index.html +146 -0
  189. package/coverage/cli/src/lib/errors/is-prompt-cancellation.ts.html +199 -0
  190. package/coverage/cli/src/lib/errors/operation-error.ts.html +325 -0
  191. package/coverage/cli/src/lib/git-guard/git-guard.ts.html +250 -0
  192. package/coverage/cli/src/lib/git-guard/index.html +131 -0
  193. package/coverage/cli/src/lib/git-guard/index.ts.html +91 -0
  194. package/coverage/cli/src/lib/git-utils/git-utils.ts.html +622 -0
  195. package/coverage/cli/src/lib/git-utils/index.html +131 -0
  196. package/coverage/cli/src/lib/git-utils/index.ts.html +115 -0
  197. package/coverage/cli/src/lib/infra-kit-config/index.html +131 -0
  198. package/coverage/cli/src/lib/infra-kit-config/index.ts.html +142 -0
  199. package/coverage/cli/src/lib/infra-kit-config/infra-kit-config.ts.html +1447 -0
  200. package/coverage/cli/src/lib/json-output/index.html +116 -0
  201. package/coverage/cli/src/lib/json-output/json-output.ts.html +226 -0
  202. package/coverage/cli/src/lib/logger/index.html +116 -0
  203. package/coverage/cli/src/lib/logger/index.ts.html +205 -0
  204. package/coverage/cli/src/lib/managed-block/index.html +131 -0
  205. package/coverage/cli/src/lib/managed-block/index.ts.html +109 -0
  206. package/coverage/cli/src/lib/managed-block/managed-block.ts.html +520 -0
  207. package/coverage/cli/src/lib/package-config/index.html +146 -0
  208. package/coverage/cli/src/lib/package-config/index.ts.html +94 -0
  209. package/coverage/cli/src/lib/package-config/package-config-schema.ts.html +142 -0
  210. package/coverage/cli/src/lib/package-config/package-config.ts.html +376 -0
  211. package/coverage/cli/src/lib/package-validator/checks/config-check.ts.html +175 -0
  212. package/coverage/cli/src/lib/package-validator/checks/files-check.ts.html +172 -0
  213. package/coverage/cli/src/lib/package-validator/checks/index.html +176 -0
  214. package/coverage/cli/src/lib/package-validator/checks/index.ts.html +97 -0
  215. package/coverage/cli/src/lib/package-validator/checks/scripts-check.ts.html +154 -0
  216. package/coverage/cli/src/lib/package-validator/checks/turbo-check.ts.html +226 -0
  217. package/coverage/cli/src/lib/package-validator/fs-utils.ts.html +139 -0
  218. package/coverage/cli/src/lib/package-validator/index.html +146 -0
  219. package/coverage/cli/src/lib/package-validator/index.ts.html +94 -0
  220. package/coverage/cli/src/lib/package-validator/loader/config-loader.ts.html +316 -0
  221. package/coverage/cli/src/lib/package-validator/loader/index.html +146 -0
  222. package/coverage/cli/src/lib/package-validator/loader/index.ts.html +91 -0
  223. package/coverage/cli/src/lib/package-validator/loader/package-discovery.ts.html +379 -0
  224. package/coverage/cli/src/lib/package-validator/package-validator.ts.html +229 -0
  225. package/coverage/cli/src/lib/path-display/index.html +131 -0
  226. package/coverage/cli/src/lib/path-display/index.ts.html +88 -0
  227. package/coverage/cli/src/lib/path-display/path-display.ts.html +211 -0
  228. package/coverage/cli/src/lib/release-id/index.html +131 -0
  229. package/coverage/cli/src/lib/release-id/index.ts.html +130 -0
  230. package/coverage/cli/src/lib/release-id/release-id.ts.html +859 -0
  231. package/coverage/cli/src/lib/release-utils/index.html +131 -0
  232. package/coverage/cli/src/lib/release-utils/index.ts.html +130 -0
  233. package/coverage/cli/src/lib/release-utils/release-utils.ts.html +778 -0
  234. package/coverage/cli/src/lib/render/index.html +116 -0
  235. package/coverage/cli/src/lib/render/render.ts.html +157 -0
  236. package/coverage/cli/src/lib/vendor/__tests__/index.html +116 -0
  237. package/coverage/cli/src/lib/vendor/__tests__/zx-mock.ts.html +181 -0
  238. package/coverage/cli/src/lib/vendor/config-schema.ts.html +319 -0
  239. package/coverage/cli/src/lib/vendor/config.ts.html +259 -0
  240. package/coverage/cli/src/lib/vendor/factory-config-schema.ts.html +190 -0
  241. package/coverage/cli/src/lib/vendor/factory-config.ts.html +358 -0
  242. package/coverage/cli/src/lib/vendor/hash.ts.html +133 -0
  243. package/coverage/cli/src/lib/vendor/index.html +251 -0
  244. package/coverage/cli/src/lib/vendor/index.ts.html +154 -0
  245. package/coverage/cli/src/lib/vendor/manifest.ts.html +538 -0
  246. package/coverage/cli/src/lib/vendor/skip-sets.ts.html +229 -0
  247. package/coverage/cli/src/lib/vendor/sync-ops.ts.html +955 -0
  248. package/coverage/cli/src/lib/vendor/walk.ts.html +253 -0
  249. package/coverage/cli/src/lib/version-utils/index.html +161 -0
  250. package/coverage/cli/src/lib/version-utils/index.ts.html +139 -0
  251. package/coverage/cli/src/lib/version-utils/load-existing-versions.ts.html +343 -0
  252. package/coverage/cli/src/lib/version-utils/next-version.ts.html +934 -0
  253. package/coverage/cli/src/lib/version-utils/version-utils.ts.html +178 -0
  254. package/coverage/cli/src/lib/worktrees/index.html +131 -0
  255. package/coverage/cli/src/lib/worktrees/index.ts.html +88 -0
  256. package/coverage/cli/src/lib/worktrees/remove-worktrees.ts.html +283 -0
  257. package/coverage/cli/src/tui/boot.tsx.html +265 -0
  258. package/coverage/cli/src/tui/index.html +116 -0
  259. package/coverage/cli/src/tui/screens/command-palette.tsx.html +562 -0
  260. package/coverage/cli/src/tui/screens/index.html +116 -0
  261. package/coverage/cli/src/types.ts.html +283 -0
  262. package/coverage/clover.xml +4488 -0
  263. package/coverage/coverage-final.json +156 -0
  264. package/coverage/favicon.png +0 -0
  265. package/coverage/index.html +1076 -0
  266. package/coverage/prettify.css +1 -0
  267. package/coverage/prettify.js +2 -0
  268. package/coverage/sort-arrow-sprite.png +0 -0
  269. package/coverage/sorter.js +210 -0
  270. package/dist/chunk-DB6CTNBS.js +3 -0
  271. package/dist/chunk-DB6CTNBS.js.map +7 -0
  272. package/dist/chunk-QBHCWAAG.js +164 -0
  273. package/dist/chunk-QBHCWAAG.js.map +7 -0
  274. package/dist/chunk-YSBF5C7C.js +2 -0
  275. package/dist/chunk-YSBF5C7C.js.map +7 -0
  276. package/dist/cli.js +5 -5
  277. package/dist/cli.js.map +3 -3
  278. package/dist/dev-server.js +13 -0
  279. package/dist/dev-server.js.map +7 -0
  280. package/dist/index.js +1 -1
  281. package/dist/mcp.js +1 -1
  282. package/dist/mcp.js.map +1 -1
  283. package/package.json +7 -2
  284. package/scripts/build.js +4 -4
  285. package/src/.omc/state/agent-replay-2ac0dcd0-01f7-41a8-a8c9-f2195599817b.jsonl +5 -0
  286. package/src/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/mission-state.json +79 -0
  287. package/src/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/pre-tool-advisory-throttle.json +26 -0
  288. package/src/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/subagent-tracking-state.json +26 -0
  289. package/src/commands/audit/.omc/state/agent-replay-f9c17a0e-2dd8-4f7d-badf-631f266c5d4c.jsonl +4 -0
  290. package/src/commands/audit/.omc/state/sessions/f9c17a0e-2dd8-4f7d-badf-631f266c5d4c/mission-state.json +79 -0
  291. package/src/commands/audit/.omc/state/sessions/f9c17a0e-2dd8-4f7d-badf-631f266c5d4c/pre-tool-advisory-throttle.json +22 -0
  292. package/src/commands/audit/.omc/state/sessions/f9c17a0e-2dd8-4f7d-badf-631f266c5d4c/subagent-tracking-state.json +26 -0
  293. package/src/commands/audit/audit.ts +1 -1
  294. package/src/commands/doctor/doctor.ts +53 -0
  295. package/src/commands/env-autoload/env-autoload.ts +14 -2
  296. package/src/commands/env-clear/env-clear.ts +48 -7
  297. package/src/commands/env-load/env-load.ts +23 -1
  298. package/src/commands/init/__tests__/shell-body.test.ts +104 -3
  299. package/src/commands/init/init.ts +91 -9
  300. package/src/dev/__tests__/dev-server-options.test.ts +57 -0
  301. package/src/dev/__tests__/dev-server.test.ts +420 -0
  302. package/src/dev/__tests__/discovery.test.ts +181 -0
  303. package/src/dev/__tests__/fixtures.ts +257 -0
  304. package/src/dev/__tests__/ports.test.ts +98 -0
  305. package/src/dev/__tests__/serverless-local-run.test.ts +533 -0
  306. package/src/dev/dev-server.ts +796 -0
  307. package/src/dev/discovery.ts +229 -0
  308. package/src/dev/interfaces.ts +11 -0
  309. package/src/dev/managed-child.ts +73 -0
  310. package/src/dev/ports.ts +100 -0
  311. package/src/dev/serverless-local-run.ts +329 -0
  312. package/src/dev/turbo-watch.ts +62 -0
  313. package/src/dev/ui-dev.ts +62 -0
  314. package/src/entry/cli.ts +25 -6
  315. package/src/entry/dev-server.ts +103 -0
  316. package/src/lib/.omc/state/sessions/f9c17a0e-2dd8-4f7d-badf-631f266c5d4c/pre-tool-advisory-throttle.json +18 -0
  317. package/src/lib/command-catalog/__tests__/__snapshots__/command-catalog.test.ts.snap +1 -0
  318. package/src/lib/command-catalog/command-catalog.ts +4 -0
  319. package/src/lib/constants/constants.ts +38 -0
  320. package/src/lib/constants/index.ts +4 -0
  321. package/src/lib/env-autoload/__tests__/env-autoload.test.ts +25 -0
  322. package/src/lib/env-autoload/env-autoload.ts +35 -4
  323. package/src/lib/infra-kit-config/index.ts +2 -0
  324. package/src/lib/infra-kit-config/infra-kit-config.ts +22 -0
  325. package/src/lib/package-config/package-config.ts +2 -2
  326. package/src/lib/package-validator/.omc/state/sessions/f9c17a0e-2dd8-4f7d-badf-631f266c5d4c/pre-tool-advisory-throttle.json +10 -0
  327. package/src/lib/package-validator/__tests__/package-validator.test.ts +9 -2
  328. package/src/lib/warm-cache/__tests__/warm-cache.test.ts +164 -0
  329. package/src/lib/warm-cache/index.ts +7 -0
  330. package/src/lib/warm-cache/warm-cache.ts +136 -0
  331. package/tsconfig.tsbuildinfo +1 -1
  332. package/dist/chunk-EY5LDHXC.js +0 -164
  333. package/dist/chunk-EY5LDHXC.js.map +0 -7
  334. package/dist/chunk-WA4BQRDC.js +0 -2
  335. package/dist/chunk-WA4BQRDC.js.map +0 -7
@@ -0,0 +1,181 @@
1
+ import * as fs from 'node:fs'
2
+ import * as path from 'node:path'
3
+ import { afterEach, describe, expect, it } from 'vitest'
4
+
5
+ import {
6
+ classifyDistChange,
7
+ discoverApiApps,
8
+ discoverUiApps,
9
+ findMonorepoRoot,
10
+ getAppDistDirs,
11
+ getPackageDistDirs,
12
+ getPackageName,
13
+ normalizeAppInclude,
14
+ } from 'src/dev/discovery'
15
+
16
+ import { createTempTracker, makeMonorepo } from './fixtures'
17
+
18
+ /**
19
+ * Filesystem discovery, driven by an explicit `root` argument — no chdir, no cast.
20
+ * Each spec builds a hermetic monorepo fixture and passes its path straight in.
21
+ */
22
+ const temp = createTempTracker()
23
+
24
+ afterEach(() => {
25
+ temp.cleanup()
26
+ })
27
+
28
+ describe('discoverApiApps — app discovery', () => {
29
+ it('discovers every apps/<app> that has an api/serverless.yml', () => {
30
+ const root = temp.register(makeMonorepo([{ name: 'client' }, { name: 'backoffice' }]))
31
+
32
+ const names = discoverApiApps(root)
33
+ .map((a) => {
34
+ return a.name
35
+ })
36
+ .sort()
37
+
38
+ expect(names).toEqual(['backoffice', 'client'])
39
+ })
40
+
41
+ it('ignores app folders that have no serverless.yml', () => {
42
+ const root = temp.register(makeMonorepo([{ name: 'client' }, { name: 'web', api: false }]))
43
+
44
+ const names = discoverApiApps(root).map((a) => {
45
+ return a.name
46
+ })
47
+
48
+ expect(names).toEqual(['client'])
49
+ })
50
+
51
+ it('reads packageName from package.json, falling back to the folder name', () => {
52
+ const root = temp.register(
53
+ makeMonorepo([{ name: 'client', packageName: 'sls-trvl-client' }, { name: 'backoffice' }]),
54
+ )
55
+
56
+ const byName = new Map(
57
+ discoverApiApps(root).map((a) => {
58
+ return [a.name, a.packageName]
59
+ }),
60
+ )
61
+
62
+ expect(byName.get('client')).toBe('sls-trvl-client')
63
+ expect(byName.get('backoffice')).toBe('backoffice')
64
+ })
65
+ })
66
+
67
+ describe('findMonorepoRoot — walk up to pnpm-workspace.yaml', () => {
68
+ it('returns the dir that holds pnpm-workspace.yaml when starting from a nested api dir', () => {
69
+ const root = temp.register(makeMonorepo([{ name: 'client' }]))
70
+ const startDir = path.join(root, 'apps', 'client', 'api')
71
+
72
+ expect(findMonorepoRoot(startDir)).toBe(root)
73
+ })
74
+ })
75
+
76
+ describe('getPackageName — package.json name with fallback', () => {
77
+ it('returns the package.json name field when present', () => {
78
+ const root = temp.register(makeMonorepo([{ name: 'client', packageName: 'sls-trvl-client' }]))
79
+ const apiDir = path.join(root, 'apps', 'client', 'api')
80
+
81
+ expect(getPackageName(apiDir, 'client')).toBe('sls-trvl-client')
82
+ })
83
+
84
+ it('falls back to the given app name when there is no package.json', () => {
85
+ const root = temp.register(makeMonorepo([{ name: 'backoffice' }]))
86
+ const apiDir = path.join(root, 'apps', 'backoffice', 'api')
87
+
88
+ expect(getPackageName(apiDir, 'backoffice')).toBe('backoffice')
89
+ })
90
+ })
91
+
92
+ describe('normalizeAppInclude — --app list normalization', () => {
93
+ it('normalizes empty, whitespace-only, undefined, and null lists to null', () => {
94
+ expect(normalizeAppInclude([])).toBeNull()
95
+ expect(normalizeAppInclude([''])).toBeNull()
96
+ expect(normalizeAppInclude(undefined)).toBeNull()
97
+ expect(normalizeAppInclude(null)).toBeNull()
98
+ })
99
+
100
+ it('keeps a non-empty list and drops falsy entries', () => {
101
+ expect(normalizeAppInclude(['client', ''])).toEqual(['client'])
102
+ })
103
+ })
104
+
105
+ describe('classifyDistChange — route a compiled-output path to app vs package', () => {
106
+ const appDistDirs = ['/repo/apps/client/api/dist']
107
+ const packageDistDirs = ['/repo/packages/lib-core/dist']
108
+
109
+ it('classifies an app-dist path as an app change and returns the matched app dist dir', () => {
110
+ const change = classifyDistChange('/repo/apps/client/api/dist/controllers/x.js', appDistDirs, packageDistDirs)
111
+
112
+ expect(change).toEqual({ kind: 'app', app: '/repo/apps/client/api/dist' })
113
+ })
114
+
115
+ it('classifies a package-dist path as a package change (a lib was rebuilt → restart all)', () => {
116
+ const change = classifyDistChange('/repo/packages/lib-core/dist/index.js', appDistDirs, packageDistDirs)
117
+
118
+ expect(change).toEqual({ kind: 'package' })
119
+ })
120
+
121
+ it('returns an app change with undefined dir when nothing matches', () => {
122
+ const change = classifyDistChange('/repo/elsewhere/dist/x.js', appDistDirs, packageDistDirs)
123
+
124
+ expect(change).toEqual({ kind: 'app', app: undefined })
125
+ })
126
+ })
127
+
128
+ describe('getPackageDistDirs / getAppDistDirs — existing dist dirs only', () => {
129
+ it('returns packages/<pkg>/dist dirs that exist and skips those without a dist', () => {
130
+ const root = temp.register(makeMonorepo([{ name: 'client', withHandler: true }]))
131
+
132
+ // makeMonorepo only scaffolds apps/*; add a packages/ tree with one built + one unbuilt pkg.
133
+ fs.mkdirSync(path.join(root, 'packages', 'lib-core', 'dist'), { recursive: true })
134
+ fs.mkdirSync(path.join(root, 'packages', 'lib-unbuilt', 'src'), { recursive: true })
135
+
136
+ const distDirs = getPackageDistDirs(root)
137
+
138
+ expect(distDirs).toContain(path.join(root, 'packages', 'lib-core', 'dist'))
139
+ expect(distDirs).not.toContain(path.join(root, 'packages', 'lib-unbuilt', 'dist'))
140
+ })
141
+
142
+ it('returns <app.path>/dist dirs that exist (withHandler apps have one)', () => {
143
+ const root = temp.register(makeMonorepo([{ name: 'client', withHandler: true }]))
144
+ const apiDir = path.join(root, 'apps', 'client', 'api')
145
+
146
+ const distDirs = getAppDistDirs([{ path: apiDir }])
147
+
148
+ expect(distDirs).toEqual([path.join(apiDir, 'dist')])
149
+ })
150
+ })
151
+
152
+ describe('discoverUiApps — frontends with a `dev` script', () => {
153
+ it('discovers apps/<app>/ui that declare a dev script, resolving the package name', () => {
154
+ const root = temp.register(
155
+ makeMonorepo([
156
+ { name: 'backoffice', ui: { packageName: 'backoffice-ui' } },
157
+ { name: 'client', ui: { packageName: 'website-ui' } },
158
+ ]),
159
+ )
160
+
161
+ const uiApps = discoverUiApps(root)
162
+
163
+ expect(uiApps).toEqual([
164
+ { name: 'backoffice', packageName: 'backoffice-ui', path: path.join(root, 'apps', 'backoffice', 'ui') },
165
+ { name: 'client', packageName: 'website-ui', path: path.join(root, 'apps', 'client', 'ui') },
166
+ ])
167
+ })
168
+
169
+ it('ignores a ui folder that has no dev script', () => {
170
+ const root = temp.register(makeMonorepo([{ name: 'seo', ui: { dev: false } }]))
171
+
172
+ expect(discoverUiApps(root)).toEqual([])
173
+ })
174
+
175
+ it('returns [] when there is no apps/ dir (UIs are optional, unlike api discovery)', () => {
176
+ const root = temp.register(makeMonorepo([]))
177
+
178
+ fs.rmSync(path.join(root, 'apps'), { recursive: true, force: true })
179
+ expect(discoverUiApps(root)).toEqual([])
180
+ })
181
+ })
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Shared test fixtures for the dev-server suites.
3
+ *
4
+ * Everything here is hermetic: temp dirs are created under the OS tmp dir and
5
+ * returned to the caller (register them with a {@link createTempTracker} so an
6
+ * `afterEach` can remove them). No module-level side effects — importing this
7
+ * file never touches the filesystem, cwd, or env.
8
+ */
9
+ import * as fs from 'node:fs'
10
+ import * as net from 'node:net'
11
+ import * as os from 'node:os'
12
+ import * as path from 'node:path'
13
+ import process from 'node:process'
14
+ import { vi } from 'vitest'
15
+
16
+ import { ServerlessLocalRun } from 'src/dev/serverless-local-run'
17
+
18
+ /** Default URL prefix the fixtures serve routes under (matches the runner default). */
19
+ export const PREFIX = '/api/v1'
20
+
21
+ /** serverless.yml exposing ping (GET), echo (POST) and item (GET {id}) routes. */
22
+ const SERVERLESS_YML = [
23
+ 'service: testapp',
24
+ 'functions:',
25
+ ' ping:',
26
+ ' handler: dist/handler.ping',
27
+ ' events:',
28
+ ' - http:',
29
+ ' method: get',
30
+ ' path: ping',
31
+ ' echo:',
32
+ ' handler: dist/handler.echo',
33
+ ' events:',
34
+ ' - http:',
35
+ ' method: post',
36
+ ' path: echo',
37
+ ' item:',
38
+ ' handler: dist/handler.item',
39
+ ' events:',
40
+ ' - http:',
41
+ ' method: get',
42
+ ' path: item/{id}',
43
+ '',
44
+ ].join('\n')
45
+
46
+ /** serverless.yml with a single ping (GET) route — used for monorepo app fixtures. */
47
+ const MONOREPO_SERVERLESS_YML = [
48
+ 'service: svc',
49
+ 'functions:',
50
+ ' ping:',
51
+ ' handler: dist/handler.ping',
52
+ ' events:',
53
+ ' - http:',
54
+ ' method: get',
55
+ ' path: ping',
56
+ '',
57
+ ].join('\n')
58
+
59
+ /**
60
+ * Compiled ESM handler written straight to `dist/*.js` (no build step). Each handler
61
+ * echoes the Lambda event back as its JSON body so the HTTP->Lambda transform can be
62
+ * asserted from the response. `version` lets a reload test tell v1 from v2 apart.
63
+ */
64
+ export function handlerSource(version = 1): string {
65
+ return [
66
+ `const version = ${version}`,
67
+ 'const reply = (event, extra) => ({',
68
+ ' statusCode: 200,',
69
+ " headers: { 'content-type': 'application/json', 'x-handler-version': String(version) },",
70
+ ' body: JSON.stringify({ version, event, ...extra }),',
71
+ '})',
72
+ 'export const ping = async (event) => reply(event, { route: "ping" })',
73
+ 'export const echo = async (event) => reply(event, { route: "echo" })',
74
+ 'export const item = async (event) => reply(event, { route: "item" })',
75
+ '',
76
+ ].join('\n')
77
+ }
78
+
79
+ /** Create an `apps/testapp/api`-shaped fixture with serverless.yml + a compiled dist handler. */
80
+ export function makeApiFixture(handlerVersion = 1): string {
81
+ const apiDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ik-sls-run-'))
82
+
83
+ fs.writeFileSync(path.join(apiDir, 'serverless.yml'), SERVERLESS_YML)
84
+ // type:module so Node loads the plain dist/*.js as ESM (named exports available).
85
+ fs.writeFileSync(path.join(apiDir, 'package.json'), JSON.stringify({ name: 'testapp-api', type: 'module' }))
86
+ fs.mkdirSync(path.join(apiDir, 'dist'), { recursive: true })
87
+ fs.writeFileSync(path.join(apiDir, 'dist', 'handler.js'), handlerSource(handlerVersion))
88
+
89
+ return apiDir
90
+ }
91
+
92
+ /** One app inside a monorepo fixture. */
93
+ export interface AppSpec {
94
+ /** App folder name (e.g. client, backoffice). */
95
+ name: string
96
+ /** package.json `name`. Omit to leave package.json out (discovery falls back to the folder name). */
97
+ packageName?: string
98
+ /** Set false to create the app folder WITHOUT serverless.yml (should be ignored by discovery). */
99
+ api?: boolean
100
+ /** Also write package.json (type:module) + a real dist/handler.js so the app can actually boot. */
101
+ withHandler?: boolean
102
+ /**
103
+ * Also scaffold `apps/<name>/ui/package.json`. `dev` (default true) controls whether it declares a
104
+ * `scripts.dev` — a UI without one must be ignored by {@link discoverUiApps}.
105
+ */
106
+ ui?: { packageName?: string; dev?: boolean }
107
+ }
108
+
109
+ /** Build a hermetic monorepo: pnpm-workspace.yaml + `apps/<app>/api/serverless.yml` per spec. */
110
+ export function makeMonorepo(apps: AppSpec[]): string {
111
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ik-monorepo-'))
112
+
113
+ fs.writeFileSync(path.join(root, 'pnpm-workspace.yaml'), 'packages:\n - apps/*\n')
114
+ fs.mkdirSync(path.join(root, 'apps'), { recursive: true })
115
+
116
+ for (const app of apps) {
117
+ const apiDir = path.join(root, 'apps', app.name, 'api')
118
+
119
+ fs.mkdirSync(apiDir, { recursive: true })
120
+ if (app.api !== false) {
121
+ fs.writeFileSync(path.join(apiDir, 'serverless.yml'), MONOREPO_SERVERLESS_YML)
122
+ }
123
+ if (app.packageName != null || app.withHandler) {
124
+ fs.writeFileSync(
125
+ path.join(apiDir, 'package.json'),
126
+ JSON.stringify({ name: app.packageName ?? `${app.name}-api`, type: 'module' }),
127
+ )
128
+ }
129
+ if (app.withHandler) {
130
+ fs.mkdirSync(path.join(apiDir, 'dist'), { recursive: true })
131
+ fs.writeFileSync(path.join(apiDir, 'dist', 'handler.js'), handlerSource(1))
132
+ }
133
+ if (app.ui) {
134
+ const uiDir = path.join(root, 'apps', app.name, 'ui')
135
+
136
+ fs.mkdirSync(uiDir, { recursive: true })
137
+ const pkg: { name: string; scripts?: Record<string, string> } = {
138
+ name: app.ui.packageName ?? `${app.name}-ui`,
139
+ }
140
+
141
+ if (app.ui.dev !== false) pkg.scripts = { dev: 'vite' }
142
+ fs.writeFileSync(path.join(uiDir, 'package.json'), JSON.stringify(pkg))
143
+ }
144
+ }
145
+
146
+ return root
147
+ }
148
+
149
+ /** Ask the OS for a currently-free TCP port on localhost, then release it. */
150
+ export async function getFreePort(): Promise<number> {
151
+ return new Promise((resolve, reject) => {
152
+ const srv = net.createServer()
153
+
154
+ srv.unref()
155
+ srv.on('error', reject)
156
+ srv.listen(0, '127.0.0.1', () => {
157
+ const addr = srv.address()
158
+ const port = typeof addr === 'object' && addr != null ? addr.port : 0
159
+
160
+ srv.close(() => {
161
+ return resolve(port)
162
+ })
163
+ })
164
+ })
165
+ }
166
+
167
+ /** True if a fresh listener can bind `port` — i.e. the previous server released it. */
168
+ export async function canBind(port: number): Promise<boolean> {
169
+ return new Promise((resolve) => {
170
+ const srv = net.createServer()
171
+
172
+ srv.once('error', () => {
173
+ return resolve(false)
174
+ })
175
+ srv.listen(port, '127.0.0.1', () => {
176
+ return srv.close(() => {
177
+ return resolve(true)
178
+ })
179
+ })
180
+ })
181
+ }
182
+
183
+ /**
184
+ * Boot a server against `apiDir` on `port`. No `chdir`: `ServerlessLocalRun` reads
185
+ * `serverless.yml` and imports the handler from `controllersPath` (absolute), so booting
186
+ * is cwd-independent. The caller owns the returned server's lifecycle (call `close()` in teardown).
187
+ */
188
+ export async function boot(
189
+ apiDir: string,
190
+ port: number,
191
+ appName = 'testapp',
192
+ prefixUrl = PREFIX,
193
+ ): Promise<ServerlessLocalRun> {
194
+ const server = new ServerlessLocalRun({ controllersPath: apiDir, prefixUrl, port, appName })
195
+
196
+ await server.start()
197
+
198
+ return server
199
+ }
200
+
201
+ /** A registry of temp dirs so an `afterEach` can remove everything created in a test. */
202
+ export interface TempTracker {
203
+ /** Register a dir for cleanup and return it (so calls can be inlined). */
204
+ register: (dir: string) => string
205
+ /** Remove every registered dir and clear the registry. */
206
+ cleanup: () => void
207
+ }
208
+
209
+ export function createTempTracker(): TempTracker {
210
+ const dirs: string[] = []
211
+
212
+ return {
213
+ register: (dir: string): string => {
214
+ dirs.push(dir)
215
+
216
+ return dir
217
+ },
218
+ cleanup: (): void => {
219
+ for (const dir of dirs.splice(0)) {
220
+ fs.rmSync(dir, { recursive: true, force: true })
221
+ }
222
+ },
223
+ }
224
+ }
225
+
226
+ /** Snapshot `process.env` so tests can restore it after mutating per-app port vars. */
227
+ export function snapshotEnv(): NodeJS.ProcessEnv {
228
+ return { ...process.env }
229
+ }
230
+
231
+ /** Restore a `process.env` snapshot taken by {@link snapshotEnv}. */
232
+ export function restoreEnv(snapshot: NodeJS.ProcessEnv): void {
233
+ process.env = { ...snapshot }
234
+ }
235
+
236
+ /** Snapshot the current working directory (fixtures chdir for serverless.yml resolution). */
237
+ export function snapshotCwd(): string {
238
+ return process.cwd()
239
+ }
240
+
241
+ /** Restore a cwd snapshot taken by {@link snapshotCwd}. */
242
+ export function restoreCwd(dir: string): void {
243
+ process.chdir(dir)
244
+ }
245
+
246
+ /**
247
+ * Spy `process.stdout.write`, pushing every written chunk into `sink`. The caller owns
248
+ * the returned spy's lifecycle (`spy.mockRestore()` in a `finally`). Used by the dev-server
249
+ * table / route-dump / request-log tests to assert on captured startup + request output.
250
+ */
251
+ export function spyStdoutWrite(sink: string[]): ReturnType<typeof vi.spyOn> {
252
+ return vi.spyOn(process.stdout, 'write').mockImplementation(((chunk: unknown): boolean => {
253
+ sink.push(String(chunk))
254
+
255
+ return true
256
+ }) as never)
257
+ }
@@ -0,0 +1,98 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import {
4
+ DEFAULT_PORT,
5
+ DEFAULT_PREFIX_URL,
6
+ findPortConflicts,
7
+ parsePortString,
8
+ resolvePort,
9
+ resolvePrefixUrl,
10
+ } from 'src/dev/ports'
11
+
12
+ /**
13
+ * Pure port / prefix resolution. Every function is side-effect free, so these
14
+ * specs pass `env` and `devConfig` in as plain objects — no fixtures, no cwd,
15
+ * no process.env mutation.
16
+ */
17
+ describe('resolvePort — precedence order', () => {
18
+ it('falls back to the 3010 default when nothing is set', () => {
19
+ expect(resolvePort('client', {}, {})).toBe(DEFAULT_PORT)
20
+ })
21
+
22
+ it('uses dev.<app>.port from config over the default', () => {
23
+ expect(resolvePort('client', {}, { client: { port: 3200 } })).toBe(3200)
24
+ })
25
+
26
+ it('lets PORT beat the config port', () => {
27
+ expect(resolvePort('client', { PORT: '3300' }, { client: { port: 3200 } })).toBe(3300)
28
+ })
29
+
30
+ it('lets {APP}_PORT beat PORT and config', () => {
31
+ expect(resolvePort('client', { PORT: '3300', CLIENT_PORT: '3400' }, { client: { port: 3200 } })).toBe(3400)
32
+ })
33
+
34
+ it('maps a hyphenated app name to UPPER_SNAKE_CASE for the {APP}_PORT key', () => {
35
+ expect(resolvePort('search-engine', { SEARCH_ENGINE_PORT: '3500' }, {})).toBe(3500)
36
+ })
37
+
38
+ it('honors {APP}_PORT = 0 and does NOT fall through to a lower tier', () => {
39
+ // 0 is a valid "pick a free port" request; only null/undefined means "unset".
40
+ expect(resolvePort('client', { CLIENT_PORT: '0' }, { client: { port: 3200 } })).toBe(0)
41
+ })
42
+
43
+ it('strips surrounding quotes on a {APP}_PORT env value', () => {
44
+ // Secrets managers can export values wrapped in quotes.
45
+ expect(resolvePort('client', { CLIENT_PORT: '"3400"' }, {})).toBe(3400)
46
+ })
47
+ })
48
+
49
+ describe('resolvePrefixUrl — config over default', () => {
50
+ it('defaults to /api/v1 when nothing is configured', () => {
51
+ expect(resolvePrefixUrl('client', {})).toBe(DEFAULT_PREFIX_URL)
52
+ })
53
+
54
+ it('honors dev.<app>.prefixUrl over the default', () => {
55
+ expect(resolvePrefixUrl('client', { client: { prefixUrl: '/custom/v2' } })).toBe('/custom/v2')
56
+ })
57
+ })
58
+
59
+ describe('parsePortString — raw port coercion', () => {
60
+ it('strips a single pair of surrounding quotes', () => {
61
+ expect(parsePortString('"3400"')).toBe(3400)
62
+ expect(parsePortString("'3400'")).toBe(3400)
63
+ })
64
+
65
+ it('returns undefined for non-numeric input', () => {
66
+ expect(parsePortString('abc')).toBeUndefined()
67
+ })
68
+
69
+ it('returns undefined for undefined and blank input', () => {
70
+ expect(parsePortString(undefined)).toBeUndefined()
71
+ expect(parsePortString('')).toBeUndefined()
72
+ })
73
+ })
74
+
75
+ describe('findPortConflicts — duplicate detection', () => {
76
+ it('reports the duplicate port and every colliding app when two apps share a port', () => {
77
+ const result = findPortConflicts([
78
+ { name: 'client', port: 3010 },
79
+ { name: 'backoffice', port: 3010 },
80
+ ])
81
+
82
+ expect(result.duplicatePorts).toEqual([3010])
83
+ expect(result.conflictingApps).toEqual([
84
+ { name: 'client', port: 3010 },
85
+ { name: 'backoffice', port: 3010 },
86
+ ])
87
+ })
88
+
89
+ it('reports no conflict when every app resolves to a distinct port', () => {
90
+ const result = findPortConflicts([
91
+ { name: 'client', port: 3010 },
92
+ { name: 'backoffice', port: 3011 },
93
+ ])
94
+
95
+ expect(result.duplicatePorts).toEqual([])
96
+ expect(result.conflictingApps).toEqual([])
97
+ })
98
+ })