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.
- package/.eslintcache +1 -1
- package/.omc/state/agent-replay-036daa78-5fee-4c7d-9b44-53584acb0d87.jsonl +26 -0
- package/.omc/state/agent-replay-0a65ec83-9de7-458d-9d0f-e56e1a26c68e.jsonl +2 -0
- package/.omc/state/agent-replay-1b6ffc57-2d9b-4af4-b603-f1cef7e01b0a.jsonl +17 -0
- package/.omc/state/agent-replay-2ac0dcd0-01f7-41a8-a8c9-f2195599817b.jsonl +5 -0
- package/.omc/state/agent-replay-d02e6565-5168-40c8-8629-de219a29fa43.jsonl +2 -0
- package/.omc/state/idle-notif-cooldown.json +1 -1
- package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/mission-state.json +129 -0
- package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/pre-tool-advisory-throttle.json +34 -0
- package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/ralph-state.json +13 -0
- package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/ralplan-state.json +11 -0
- package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/subagent-tracking-state.json +80 -0
- package/.omc/state/sessions/036daa78-5fee-4c7d-9b44-53584acb0d87/ultrawork-state.json +11 -0
- package/.omc/state/sessions/0a65ec83-9de7-458d-9d0f-e56e1a26c68e/pre-tool-advisory-throttle.json +18 -0
- package/.omc/state/sessions/0a65ec83-9de7-458d-9d0f-e56e1a26c68e/skill-active-state.json +15 -0
- package/.omc/state/sessions/1b6ffc57-2d9b-4af4-b603-f1cef7e01b0a/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/1b6ffc57-2d9b-4af4-b603-f1cef7e01b0a/mission-state.json +139 -0
- package/.omc/state/sessions/1b6ffc57-2d9b-4af4-b603-f1cef7e01b0a/pre-tool-advisory-throttle.json +50 -0
- package/.omc/state/sessions/1b6ffc57-2d9b-4af4-b603-f1cef7e01b0a/ralplan-state.json +11 -0
- package/.omc/state/sessions/1b6ffc57-2d9b-4af4-b603-f1cef7e01b0a/subagent-tracking-state.json +90 -0
- package/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/mission-state.json +79 -0
- package/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/pre-tool-advisory-throttle.json +10 -0
- package/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/skill-active-state.json +15 -0
- package/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/subagent-tracking-state.json +26 -0
- package/.omc/state/sessions/7772f972-4ec1-4287-bf42-a92c14cf3ffc/pre-tool-advisory-throttle.json +10 -0
- package/.omc/state/sessions/d02e6565-5168-40c8-8629-de219a29fa43/last-tool-error-state.json +7 -0
- package/.omc/state/sessions/d02e6565-5168-40c8-8629-de219a29fa43/mission-state.json +53 -0
- package/.omc/state/sessions/d02e6565-5168-40c8-8629-de219a29fa43/pre-tool-advisory-throttle.json +18 -0
- package/.omc/state/sessions/d02e6565-5168-40c8-8629-de219a29fa43/subagent-tracking-state.json +17 -0
- package/.omc/state/sessions/f9c17a0e-2dd8-4f7d-badf-631f266c5d4c/pre-tool-advisory-throttle.json +10 -0
- package/.turbo/turbo-build.log +7 -6
- package/.turbo/turbo-check.log +1 -1
- package/.turbo/turbo-eslint-check.log +2 -1
- package/.turbo/turbo-infra-kit-check.log +14 -0
- package/.turbo/turbo-prettier-check.log +1 -1
- package/.turbo/turbo-test.log +406 -128
- package/.turbo/turbo-ts-check.log +1 -1
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/cli/index.html +116 -0
- package/coverage/cli/package.json.html +283 -0
- package/coverage/cli/src/commands/audit/audit.ts.html +616 -0
- package/coverage/cli/src/commands/audit/index.html +131 -0
- package/coverage/cli/src/commands/audit/index.ts.html +88 -0
- package/coverage/cli/src/commands/doctor/doctor.ts.html +1654 -0
- package/coverage/cli/src/commands/doctor/index.html +131 -0
- package/coverage/cli/src/commands/doctor/index.ts.html +88 -0
- package/coverage/cli/src/commands/env-clear/env-clear.ts.html +370 -0
- package/coverage/cli/src/commands/env-clear/index.html +131 -0
- package/coverage/cli/src/commands/env-clear/index.ts.html +88 -0
- package/coverage/cli/src/commands/env-list/env-list.ts.html +229 -0
- package/coverage/cli/src/commands/env-list/index.html +131 -0
- package/coverage/cli/src/commands/env-list/index.ts.html +88 -0
- package/coverage/cli/src/commands/env-load/env-load.ts.html +1201 -0
- package/coverage/cli/src/commands/env-load/index.html +131 -0
- package/coverage/cli/src/commands/env-load/index.ts.html +91 -0
- package/coverage/cli/src/commands/env-status/env-status.ts.html +409 -0
- package/coverage/cli/src/commands/env-status/index.html +131 -0
- package/coverage/cli/src/commands/env-status/index.ts.html +88 -0
- package/coverage/cli/src/commands/gh-merge-dev/gh-merge-dev.ts.html +694 -0
- package/coverage/cli/src/commands/gh-merge-dev/index.html +131 -0
- package/coverage/cli/src/commands/gh-merge-dev/index.ts.html +88 -0
- package/coverage/cli/src/commands/gh-release-deliver/gh-release-deliver.ts.html +1495 -0
- package/coverage/cli/src/commands/gh-release-deliver/index.html +131 -0
- package/coverage/cli/src/commands/gh-release-deliver/index.ts.html +88 -0
- package/coverage/cli/src/commands/gh-release-deploy-all/gh-release-deploy-all.ts.html +706 -0
- package/coverage/cli/src/commands/gh-release-deploy-all/index.html +131 -0
- package/coverage/cli/src/commands/gh-release-deploy-all/index.ts.html +88 -0
- package/coverage/cli/src/commands/gh-release-deploy-selected/gh-release-deploy-selected.ts.html +994 -0
- package/coverage/cli/src/commands/gh-release-deploy-selected/index.html +131 -0
- package/coverage/cli/src/commands/gh-release-deploy-selected/index.ts.html +88 -0
- package/coverage/cli/src/commands/gh-release-list/gh-release-list.ts.html +355 -0
- package/coverage/cli/src/commands/gh-release-list/index.html +131 -0
- package/coverage/cli/src/commands/gh-release-list/index.ts.html +88 -0
- package/coverage/cli/src/commands/init/agent-files.ts.html +742 -0
- package/coverage/cli/src/commands/init/index.html +146 -0
- package/coverage/cli/src/commands/init/init.ts.html +1159 -0
- package/coverage/cli/src/commands/init/migrate-config.ts.html +1219 -0
- package/coverage/cli/src/commands/release-create/index.html +131 -0
- package/coverage/cli/src/commands/release-create/index.ts.html +88 -0
- package/coverage/cli/src/commands/release-create/release-create.ts.html +1426 -0
- package/coverage/cli/src/commands/release-desc-edit/index.html +131 -0
- package/coverage/cli/src/commands/release-desc-edit/index.ts.html +88 -0
- package/coverage/cli/src/commands/release-desc-edit/release-desc-edit.ts.html +766 -0
- package/coverage/cli/src/commands/vendor-check/index.html +131 -0
- package/coverage/cli/src/commands/vendor-check/index.ts.html +88 -0
- package/coverage/cli/src/commands/vendor-check/vendor-check.ts.html +466 -0
- package/coverage/cli/src/commands/vendor-config/index.html +116 -0
- package/coverage/cli/src/commands/vendor-config/vendor-config.ts.html +571 -0
- package/coverage/cli/src/commands/vendor-diff/index.html +131 -0
- package/coverage/cli/src/commands/vendor-diff/index.ts.html +88 -0
- package/coverage/cli/src/commands/vendor-diff/vendor-diff.ts.html +316 -0
- package/coverage/cli/src/commands/vendor-manifest/index.html +131 -0
- package/coverage/cli/src/commands/vendor-manifest/index.ts.html +88 -0
- package/coverage/cli/src/commands/vendor-manifest/vendor-manifest.ts.html +265 -0
- package/coverage/cli/src/commands/vendor-sync/index.html +131 -0
- package/coverage/cli/src/commands/vendor-sync/index.ts.html +88 -0
- package/coverage/cli/src/commands/vendor-sync/vendor-sync.ts.html +355 -0
- package/coverage/cli/src/commands/version/index.html +131 -0
- package/coverage/cli/src/commands/version/index.ts.html +88 -0
- package/coverage/cli/src/commands/version/version.ts.html +184 -0
- package/coverage/cli/src/commands/worktrees-add/index.html +131 -0
- package/coverage/cli/src/commands/worktrees-add/index.ts.html +88 -0
- package/coverage/cli/src/commands/worktrees-add/worktrees-add.ts.html +1183 -0
- package/coverage/cli/src/commands/worktrees-list/index.html +131 -0
- package/coverage/cli/src/commands/worktrees-list/index.ts.html +88 -0
- package/coverage/cli/src/commands/worktrees-list/worktrees-list.ts.html +400 -0
- package/coverage/cli/src/commands/worktrees-reload/index.html +131 -0
- package/coverage/cli/src/commands/worktrees-reload/index.ts.html +88 -0
- package/coverage/cli/src/commands/worktrees-reload/worktrees-reload.ts.html +775 -0
- package/coverage/cli/src/commands/worktrees-remove/index.html +131 -0
- package/coverage/cli/src/commands/worktrees-remove/index.ts.html +88 -0
- package/coverage/cli/src/commands/worktrees-remove/worktrees-remove.ts.html +682 -0
- package/coverage/cli/src/commands/worktrees-sync/index.html +131 -0
- package/coverage/cli/src/commands/worktrees-sync/index.ts.html +88 -0
- package/coverage/cli/src/commands/worktrees-sync/worktrees-sync.ts.html +679 -0
- package/coverage/cli/src/dev/__tests__/fixtures.ts.html +787 -0
- package/coverage/cli/src/dev/__tests__/index.html +116 -0
- package/coverage/cli/src/dev/dev-server.ts.html +1855 -0
- package/coverage/cli/src/dev/discovery.ts.html +598 -0
- package/coverage/cli/src/dev/index.html +191 -0
- package/coverage/cli/src/dev/interfaces.ts.html +223 -0
- package/coverage/cli/src/dev/logger.ts.html +1102 -0
- package/coverage/cli/src/dev/ports.ts.html +430 -0
- package/coverage/cli/src/dev/serverless-local-run.ts.html +916 -0
- package/coverage/cli/src/entry/dev-server.ts.html +409 -0
- package/coverage/cli/src/entry/index.html +116 -0
- package/coverage/cli/src/index.html +116 -0
- package/coverage/cli/src/integrations/cmux/canonicalize-cmux-title.ts.html +178 -0
- package/coverage/cli/src/integrations/cmux/close-workspace-by-title.ts.html +262 -0
- package/coverage/cli/src/integrations/cmux/index.html +191 -0
- package/coverage/cli/src/integrations/cmux/index.ts.html +100 -0
- package/coverage/cli/src/integrations/cmux/list-workspace-titles.ts.html +226 -0
- package/coverage/cli/src/integrations/cmux/open-workspace-with-layout.ts.html +325 -0
- package/coverage/cli/src/integrations/cmux/workspace-title.ts.html +154 -0
- package/coverage/cli/src/integrations/cursor/add-folders-to-workspace.ts.html +337 -0
- package/coverage/cli/src/integrations/cursor/index.html +206 -0
- package/coverage/cli/src/integrations/cursor/index.ts.html +103 -0
- package/coverage/cli/src/integrations/cursor/launch-cursor.ts.html +121 -0
- package/coverage/cli/src/integrations/cursor/open-cursor-workspace.ts.html +277 -0
- package/coverage/cli/src/integrations/cursor/reconcile-workspace-folders.ts.html +355 -0
- package/coverage/cli/src/integrations/cursor/remove-folders-from-workspace.ts.html +364 -0
- package/coverage/cli/src/integrations/cursor/resolve-workspace-path.ts.html +124 -0
- package/coverage/cli/src/integrations/doppler/doppler-cli-auth.ts.html +178 -0
- package/coverage/cli/src/integrations/doppler/doppler-errors.ts.html +349 -0
- package/coverage/cli/src/integrations/doppler/doppler-project.ts.html +355 -0
- package/coverage/cli/src/integrations/doppler/index.html +161 -0
- package/coverage/cli/src/integrations/doppler/index.ts.html +97 -0
- package/coverage/cli/src/integrations/gh/gh-cli-auth/gh-cli-auth.ts.html +172 -0
- package/coverage/cli/src/integrations/gh/gh-cli-auth/index.html +131 -0
- package/coverage/cli/src/integrations/gh/gh-cli-auth/index.ts.html +88 -0
- package/coverage/cli/src/integrations/gh/gh-release-prs/gh-release-prs.ts.html +703 -0
- package/coverage/cli/src/integrations/gh/gh-release-prs/index.html +131 -0
- package/coverage/cli/src/integrations/gh/gh-release-prs/index.ts.html +91 -0
- package/coverage/cli/src/integrations/gh/index.html +116 -0
- package/coverage/cli/src/integrations/gh/index.ts.html +94 -0
- package/coverage/cli/src/integrations/ide/add-ide-worktree-folders.ts.html +316 -0
- package/coverage/cli/src/integrations/ide/index.html +191 -0
- package/coverage/cli/src/integrations/ide/index.ts.html +103 -0
- package/coverage/cli/src/integrations/ide/open-ide-workspace.ts.html +238 -0
- package/coverage/cli/src/integrations/ide/provider-label.ts.html +157 -0
- package/coverage/cli/src/integrations/ide/remove-ide-worktree-folders.ts.html +520 -0
- package/coverage/cli/src/integrations/ide/types.ts.html +208 -0
- package/coverage/cli/src/integrations/jira/api.ts.html +1060 -0
- package/coverage/cli/src/integrations/jira/index.html +131 -0
- package/coverage/cli/src/integrations/jira/index.ts.html +142 -0
- package/coverage/cli/src/integrations/zed/add-folders-to-zed-workspace.ts.html +175 -0
- package/coverage/cli/src/integrations/zed/index.html +161 -0
- package/coverage/cli/src/integrations/zed/index.ts.html +94 -0
- package/coverage/cli/src/integrations/zed/open-zed-workspace.ts.html +238 -0
- package/coverage/cli/src/integrations/zed/reuse-zed-workspace.ts.html +256 -0
- package/coverage/cli/src/lib/assert-never/assert-never.ts.html +130 -0
- package/coverage/cli/src/lib/assert-never/index.html +131 -0
- package/coverage/cli/src/lib/assert-never/index.ts.html +88 -0
- package/coverage/cli/src/lib/command-catalog/command-catalog.ts.html +523 -0
- package/coverage/cli/src/lib/command-catalog/index.html +116 -0
- package/coverage/cli/src/lib/command-echo/command-echo.ts.html +319 -0
- package/coverage/cli/src/lib/command-echo/index.html +131 -0
- package/coverage/cli/src/lib/command-echo/index.ts.html +88 -0
- package/coverage/cli/src/lib/constants/constants.ts.html +472 -0
- package/coverage/cli/src/lib/constants/index.html +131 -0
- package/coverage/cli/src/lib/constants/index.ts.html +139 -0
- package/coverage/cli/src/lib/env-autoload/env-autoload.ts.html +1111 -0
- package/coverage/cli/src/lib/env-autoload/index.html +116 -0
- package/coverage/cli/src/lib/errors/format-zx-error.ts.html +247 -0
- package/coverage/cli/src/lib/errors/index.html +146 -0
- package/coverage/cli/src/lib/errors/is-prompt-cancellation.ts.html +199 -0
- package/coverage/cli/src/lib/errors/operation-error.ts.html +325 -0
- package/coverage/cli/src/lib/git-guard/git-guard.ts.html +250 -0
- package/coverage/cli/src/lib/git-guard/index.html +131 -0
- package/coverage/cli/src/lib/git-guard/index.ts.html +91 -0
- package/coverage/cli/src/lib/git-utils/git-utils.ts.html +622 -0
- package/coverage/cli/src/lib/git-utils/index.html +131 -0
- package/coverage/cli/src/lib/git-utils/index.ts.html +115 -0
- package/coverage/cli/src/lib/infra-kit-config/index.html +131 -0
- package/coverage/cli/src/lib/infra-kit-config/index.ts.html +142 -0
- package/coverage/cli/src/lib/infra-kit-config/infra-kit-config.ts.html +1447 -0
- package/coverage/cli/src/lib/json-output/index.html +116 -0
- package/coverage/cli/src/lib/json-output/json-output.ts.html +226 -0
- package/coverage/cli/src/lib/logger/index.html +116 -0
- package/coverage/cli/src/lib/logger/index.ts.html +205 -0
- package/coverage/cli/src/lib/managed-block/index.html +131 -0
- package/coverage/cli/src/lib/managed-block/index.ts.html +109 -0
- package/coverage/cli/src/lib/managed-block/managed-block.ts.html +520 -0
- package/coverage/cli/src/lib/package-config/index.html +146 -0
- package/coverage/cli/src/lib/package-config/index.ts.html +94 -0
- package/coverage/cli/src/lib/package-config/package-config-schema.ts.html +142 -0
- package/coverage/cli/src/lib/package-config/package-config.ts.html +376 -0
- package/coverage/cli/src/lib/package-validator/checks/config-check.ts.html +175 -0
- package/coverage/cli/src/lib/package-validator/checks/files-check.ts.html +172 -0
- package/coverage/cli/src/lib/package-validator/checks/index.html +176 -0
- package/coverage/cli/src/lib/package-validator/checks/index.ts.html +97 -0
- package/coverage/cli/src/lib/package-validator/checks/scripts-check.ts.html +154 -0
- package/coverage/cli/src/lib/package-validator/checks/turbo-check.ts.html +226 -0
- package/coverage/cli/src/lib/package-validator/fs-utils.ts.html +139 -0
- package/coverage/cli/src/lib/package-validator/index.html +146 -0
- package/coverage/cli/src/lib/package-validator/index.ts.html +94 -0
- package/coverage/cli/src/lib/package-validator/loader/config-loader.ts.html +316 -0
- package/coverage/cli/src/lib/package-validator/loader/index.html +146 -0
- package/coverage/cli/src/lib/package-validator/loader/index.ts.html +91 -0
- package/coverage/cli/src/lib/package-validator/loader/package-discovery.ts.html +379 -0
- package/coverage/cli/src/lib/package-validator/package-validator.ts.html +229 -0
- package/coverage/cli/src/lib/path-display/index.html +131 -0
- package/coverage/cli/src/lib/path-display/index.ts.html +88 -0
- package/coverage/cli/src/lib/path-display/path-display.ts.html +211 -0
- package/coverage/cli/src/lib/release-id/index.html +131 -0
- package/coverage/cli/src/lib/release-id/index.ts.html +130 -0
- package/coverage/cli/src/lib/release-id/release-id.ts.html +859 -0
- package/coverage/cli/src/lib/release-utils/index.html +131 -0
- package/coverage/cli/src/lib/release-utils/index.ts.html +130 -0
- package/coverage/cli/src/lib/release-utils/release-utils.ts.html +778 -0
- package/coverage/cli/src/lib/render/index.html +116 -0
- package/coverage/cli/src/lib/render/render.ts.html +157 -0
- package/coverage/cli/src/lib/vendor/__tests__/index.html +116 -0
- package/coverage/cli/src/lib/vendor/__tests__/zx-mock.ts.html +181 -0
- package/coverage/cli/src/lib/vendor/config-schema.ts.html +319 -0
- package/coverage/cli/src/lib/vendor/config.ts.html +259 -0
- package/coverage/cli/src/lib/vendor/factory-config-schema.ts.html +190 -0
- package/coverage/cli/src/lib/vendor/factory-config.ts.html +358 -0
- package/coverage/cli/src/lib/vendor/hash.ts.html +133 -0
- package/coverage/cli/src/lib/vendor/index.html +251 -0
- package/coverage/cli/src/lib/vendor/index.ts.html +154 -0
- package/coverage/cli/src/lib/vendor/manifest.ts.html +538 -0
- package/coverage/cli/src/lib/vendor/skip-sets.ts.html +229 -0
- package/coverage/cli/src/lib/vendor/sync-ops.ts.html +955 -0
- package/coverage/cli/src/lib/vendor/walk.ts.html +253 -0
- package/coverage/cli/src/lib/version-utils/index.html +161 -0
- package/coverage/cli/src/lib/version-utils/index.ts.html +139 -0
- package/coverage/cli/src/lib/version-utils/load-existing-versions.ts.html +343 -0
- package/coverage/cli/src/lib/version-utils/next-version.ts.html +934 -0
- package/coverage/cli/src/lib/version-utils/version-utils.ts.html +178 -0
- package/coverage/cli/src/lib/worktrees/index.html +131 -0
- package/coverage/cli/src/lib/worktrees/index.ts.html +88 -0
- package/coverage/cli/src/lib/worktrees/remove-worktrees.ts.html +283 -0
- package/coverage/cli/src/tui/boot.tsx.html +265 -0
- package/coverage/cli/src/tui/index.html +116 -0
- package/coverage/cli/src/tui/screens/command-palette.tsx.html +562 -0
- package/coverage/cli/src/tui/screens/index.html +116 -0
- package/coverage/cli/src/types.ts.html +283 -0
- package/coverage/clover.xml +4488 -0
- package/coverage/coverage-final.json +156 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +1076 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/chunk-DB6CTNBS.js +3 -0
- package/dist/chunk-DB6CTNBS.js.map +7 -0
- package/dist/chunk-QBHCWAAG.js +164 -0
- package/dist/chunk-QBHCWAAG.js.map +7 -0
- package/dist/chunk-YSBF5C7C.js +2 -0
- package/dist/chunk-YSBF5C7C.js.map +7 -0
- package/dist/cli.js +5 -5
- package/dist/cli.js.map +3 -3
- package/dist/dev-server.js +13 -0
- package/dist/dev-server.js.map +7 -0
- package/dist/index.js +1 -1
- package/dist/mcp.js +1 -1
- package/dist/mcp.js.map +1 -1
- package/package.json +7 -2
- package/scripts/build.js +4 -4
- package/src/.omc/state/agent-replay-2ac0dcd0-01f7-41a8-a8c9-f2195599817b.jsonl +5 -0
- package/src/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/mission-state.json +79 -0
- package/src/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/pre-tool-advisory-throttle.json +26 -0
- package/src/.omc/state/sessions/2ac0dcd0-01f7-41a8-a8c9-f2195599817b/subagent-tracking-state.json +26 -0
- package/src/commands/audit/.omc/state/agent-replay-f9c17a0e-2dd8-4f7d-badf-631f266c5d4c.jsonl +4 -0
- package/src/commands/audit/.omc/state/sessions/f9c17a0e-2dd8-4f7d-badf-631f266c5d4c/mission-state.json +79 -0
- package/src/commands/audit/.omc/state/sessions/f9c17a0e-2dd8-4f7d-badf-631f266c5d4c/pre-tool-advisory-throttle.json +22 -0
- package/src/commands/audit/.omc/state/sessions/f9c17a0e-2dd8-4f7d-badf-631f266c5d4c/subagent-tracking-state.json +26 -0
- package/src/commands/audit/audit.ts +1 -1
- package/src/commands/doctor/doctor.ts +53 -0
- package/src/commands/env-autoload/env-autoload.ts +14 -2
- package/src/commands/env-clear/env-clear.ts +48 -7
- package/src/commands/env-load/env-load.ts +23 -1
- package/src/commands/init/__tests__/shell-body.test.ts +104 -3
- package/src/commands/init/init.ts +91 -9
- package/src/dev/__tests__/dev-server-options.test.ts +57 -0
- package/src/dev/__tests__/dev-server.test.ts +420 -0
- package/src/dev/__tests__/discovery.test.ts +181 -0
- package/src/dev/__tests__/fixtures.ts +257 -0
- package/src/dev/__tests__/ports.test.ts +98 -0
- package/src/dev/__tests__/serverless-local-run.test.ts +533 -0
- package/src/dev/dev-server.ts +796 -0
- package/src/dev/discovery.ts +229 -0
- package/src/dev/interfaces.ts +11 -0
- package/src/dev/managed-child.ts +73 -0
- package/src/dev/ports.ts +100 -0
- package/src/dev/serverless-local-run.ts +329 -0
- package/src/dev/turbo-watch.ts +62 -0
- package/src/dev/ui-dev.ts +62 -0
- package/src/entry/cli.ts +25 -6
- package/src/entry/dev-server.ts +103 -0
- package/src/lib/.omc/state/sessions/f9c17a0e-2dd8-4f7d-badf-631f266c5d4c/pre-tool-advisory-throttle.json +18 -0
- package/src/lib/command-catalog/__tests__/__snapshots__/command-catalog.test.ts.snap +1 -0
- package/src/lib/command-catalog/command-catalog.ts +4 -0
- package/src/lib/constants/constants.ts +38 -0
- package/src/lib/constants/index.ts +4 -0
- package/src/lib/env-autoload/__tests__/env-autoload.test.ts +25 -0
- package/src/lib/env-autoload/env-autoload.ts +35 -4
- package/src/lib/infra-kit-config/index.ts +2 -0
- package/src/lib/infra-kit-config/infra-kit-config.ts +22 -0
- package/src/lib/package-config/package-config.ts +2 -2
- package/src/lib/package-validator/.omc/state/sessions/f9c17a0e-2dd8-4f7d-badf-631f266c5d4c/pre-tool-advisory-throttle.json +10 -0
- package/src/lib/package-validator/__tests__/package-validator.test.ts +9 -2
- package/src/lib/warm-cache/__tests__/warm-cache.test.ts +164 -0
- package/src/lib/warm-cache/index.ts +7 -0
- package/src/lib/warm-cache/warm-cache.ts +136 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/chunk-EY5LDHXC.js +0 -164
- package/dist/chunk-EY5LDHXC.js.map +0 -7
- package/dist/chunk-WA4BQRDC.js +0 -2
- package/dist/chunk-WA4BQRDC.js.map +0 -7
|
@@ -277,6 +277,7 @@ export const buildShellBody = (): string => {
|
|
|
277
277
|
return [
|
|
278
278
|
'zmodload zsh/stat 2>/dev/null',
|
|
279
279
|
'zmodload zsh/datetime 2>/dev/null',
|
|
280
|
+
'zmodload zsh/sched 2>/dev/null',
|
|
280
281
|
// eslint-disable-next-line no-template-curly-in-string
|
|
281
282
|
'if [[ -z "${INFRA_KIT_SESSION}" ]]; then',
|
|
282
283
|
' export INFRA_KIT_SESSION=$(head -c 4 /dev/urandom | xxd -p)',
|
|
@@ -287,11 +288,30 @@ export const buildShellBody = (): string => {
|
|
|
287
288
|
': ${_INFRA_KIT_LAST_CLEAR_MTIME:=0}',
|
|
288
289
|
// eslint-disable-next-line no-template-curly-in-string
|
|
289
290
|
': ${_INFRA_KIT_SHELL_STARTED:=${EPOCHSECONDS:-0}}',
|
|
290
|
-
|
|
291
|
+
// Warm-cache source TTL (seconds); MUST equal DEFAULT_WARM_TTL_SECONDS (node
|
|
292
|
+
// eviction) — both 2h. A warm file older than this is not sourced at startup.
|
|
293
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
294
|
+
': ${_INFRA_KIT_WARM_TTL:=7200}',
|
|
295
|
+
'export _INFRA_KIT_LAST_LOAD_MTIME _INFRA_KIT_LAST_CLEAR_MTIME _INFRA_KIT_SHELL_STARTED _INFRA_KIT_WARM_TTL',
|
|
291
296
|
`env-load() { local f m; f=$(${runCmd} env-load "$@") || return; m=$(zstat +mtime -- "$f" 2>/dev/null || echo 0); _INFRA_KIT_LAST_LOAD_MTIME=$m; source "$f"; ${runCmd} env-status; }`,
|
|
292
|
-
`env-clear() { local f m; f=$(${runCmd} env-clear) || return; m=$(zstat +mtime -- "$f" 2>/dev/null || echo 0); _INFRA_KIT_LAST_CLEAR_MTIME=$m; source "$f"; ${runCmd} env-status; }`,
|
|
297
|
+
`env-clear() { local f m; f=$(${runCmd} env-clear "$@") || return; m=$(zstat +mtime -- "$f" 2>/dev/null || echo 0); _INFRA_KIT_LAST_CLEAR_MTIME=$m; source "$f"; ${runCmd} env-status; }`,
|
|
293
298
|
`env-status() { ${runCmd} env-status; }`,
|
|
294
299
|
`alias ik='${runCmd}'`,
|
|
300
|
+
// Print an async notice without corrupting an already-drawn prompt. The
|
|
301
|
+
// startup poll fires via `sched` at an IDLE prompt (ZLE active) which does
|
|
302
|
+
// NOT redraw the prompt, so a bare `print` lands appended to the visible
|
|
303
|
+
// prompt line and looks like un-removable typed input. When ZLE is active we
|
|
304
|
+
// erase the current line (CR + clear-to-EOL), print the message on its own
|
|
305
|
+
// line, then `zle reset-prompt` to redraw the prompt below it. In precmd /
|
|
306
|
+
// shell-startup (ZLE inactive) `zle` is false and this is a plain stderr
|
|
307
|
+
// print above the about-to-be-drawn prompt — unchanged behavior.
|
|
308
|
+
'_infra_kit_notify() {',
|
|
309
|
+
// When ZLE is active, erase the current prompt line first and redraw after;
|
|
310
|
+
// both are no-ops when ZLE is inactive so this stays a plain stderr print.
|
|
311
|
+
" zle && print -u2 -n $'\\r\\e[K'",
|
|
312
|
+
' print -u2 -- "$1"',
|
|
313
|
+
' zle && zle reset-prompt',
|
|
314
|
+
'}',
|
|
295
315
|
'_infra_kit_autoload() {',
|
|
296
316
|
' [[ -z "$INFRA_KIT_SESSION" ]] && return',
|
|
297
317
|
// eslint-disable-next-line no-template-curly-in-string
|
|
@@ -306,18 +326,73 @@ export const buildShellBody = (): string => {
|
|
|
306
326
|
' source "$load_file"',
|
|
307
327
|
' _INFRA_KIT_LAST_LOAD_MTIME=$load_mtime',
|
|
308
328
|
// eslint-disable-next-line no-template-curly-in-string
|
|
309
|
-
'
|
|
329
|
+
' _infra_kit_notify "infra-kit: auto-loaded vars for ${INFRA_KIT_ENV_CONFIG:-?}"',
|
|
310
330
|
' fi',
|
|
311
331
|
' if (( clear_mtime > _INFRA_KIT_LAST_CLEAR_MTIME && clear_mtime >= _INFRA_KIT_SHELL_STARTED && clear_mtime > load_mtime )); then',
|
|
312
332
|
' source "$clear_file"',
|
|
313
333
|
' _INFRA_KIT_LAST_CLEAR_MTIME=$clear_mtime',
|
|
314
|
-
'
|
|
334
|
+
' _infra_kit_notify "infra-kit: auto-cleared env"',
|
|
315
335
|
' fi',
|
|
316
336
|
'}',
|
|
317
337
|
'autoload -Uz add-zsh-hook',
|
|
318
338
|
'if (( _INFRA_KIT_SHELL_STARTED > 0 )); then',
|
|
319
339
|
' add-zsh-hook precmd _infra_kit_autoload',
|
|
320
340
|
'fi',
|
|
341
|
+
// Non-blocking startup poll: the shell-startup spawn (below) writes env-load.sh
|
|
342
|
+
// asynchronously (node + Doppler fetch, ~0.5-2s), so the FIRST precmd — which
|
|
343
|
+
// fires before the first prompt — runs before the file lands and sources nothing;
|
|
344
|
+
// without this the vars only appear after the user's first command. This one-shot
|
|
345
|
+
// re-arming poll re-runs the precmd sourcer once per second, at the idle prompt
|
|
346
|
+
// with NO user command, until it sources the freshly written file (its mtime is
|
|
347
|
+
// >= _INFRA_KIT_SHELL_STARTED so the existing gate accepts it) OR the startup
|
|
348
|
+
// window closes. _infra_kit_autoload sets _INFRA_KIT_LAST_LOAD_MTIME on a
|
|
349
|
+
// successful source, which lifts it to >= _INFRA_KIT_SHELL_STARTED and stops the
|
|
350
|
+
// re-arm. Bounded to WINDOW seconds so an unconfigured project / down Doppler
|
|
351
|
+
// that never writes a file only costs a handful of no-op ticks, then falls back
|
|
352
|
+
// to the precmd hook. Sources the same freshly written file as precmd, so there
|
|
353
|
+
// is no stale/last-known-good secrets window.
|
|
354
|
+
'_INFRA_KIT_AUTOLOAD_WINDOW=6',
|
|
355
|
+
'export _INFRA_KIT_AUTOLOAD_WINDOW',
|
|
356
|
+
'_infra_kit_poll_autoload() {',
|
|
357
|
+
' _infra_kit_autoload',
|
|
358
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
359
|
+
' if (( _INFRA_KIT_LAST_LOAD_MTIME < _INFRA_KIT_SHELL_STARTED )) && (( ${EPOCHSECONDS:-0} - _INFRA_KIT_SHELL_STARTED < _INFRA_KIT_AUTOLOAD_WINDOW )); then',
|
|
360
|
+
' (( $+builtins[sched] )) && sched +1 _infra_kit_poll_autoload',
|
|
361
|
+
' fi',
|
|
362
|
+
'}',
|
|
363
|
+
// Warm cache: source the project-scoped last-known-good env-load.sh INSTANTLY at
|
|
364
|
+
// startup (pure zsh, no node/Doppler) so vars are present at prompt-0, before the
|
|
365
|
+
// backgrounded refresh lands. `key` is sha256 of the canonical project dir passed
|
|
366
|
+
// in by the caller — byte-identical to node's warmCacheKey (printf %s | shasum,
|
|
367
|
+
// full 64-hex). Skips when: no sha tool (degrade to poll), no warm file, a project
|
|
368
|
+
// clear-marker is at least as new (clear wins ties), or the file is past TTL
|
|
369
|
+
// (bounds a rotated secret served at prompt-0). Deliberately does NOT set
|
|
370
|
+
// _INFRA_KIT_LAST_LOAD_MTIME (leaves it 0) so the poll/precmd still source the
|
|
371
|
+
// fresh SESSION file (different path, newer mtime) and upgrade warm->fresh.
|
|
372
|
+
'_infra_kit_warm_source() {',
|
|
373
|
+
' local canon="$1" key',
|
|
374
|
+
' if (( $+commands[shasum] )); then',
|
|
375
|
+
' key=$(printf %s "$canon" | shasum -a 256 | cut -c1-64)',
|
|
376
|
+
' elif (( $+commands[sha256sum] )); then',
|
|
377
|
+
' key=$(printf %s "$canon" | sha256sum | cut -c1-64)',
|
|
378
|
+
' else',
|
|
379
|
+
' return',
|
|
380
|
+
' fi',
|
|
381
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
382
|
+
' local cache_root="${XDG_CACHE_HOME:-$HOME/.cache}/infra-kit"',
|
|
383
|
+
' local warm="$cache_root/projects/$key/env-load.sh"',
|
|
384
|
+
' local wclear="$cache_root/projects/$key/env-clear.sh"',
|
|
385
|
+
' [[ -f "$warm" ]] || return',
|
|
386
|
+
' local wm=0 cm=0',
|
|
387
|
+
' wm=$(zstat +mtime -- "$warm" 2>/dev/null || echo 0)',
|
|
388
|
+
' [[ -f "$wclear" ]] && cm=$(zstat +mtime -- "$wclear" 2>/dev/null || echo 0)',
|
|
389
|
+
' (( cm >= wm )) && return',
|
|
390
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
391
|
+
' (( ${EPOCHSECONDS:-0} - wm >= _INFRA_KIT_WARM_TTL )) && return',
|
|
392
|
+
' source "$warm"',
|
|
393
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
394
|
+
' _infra_kit_notify "infra-kit: loaded cached vars for ${INFRA_KIT_ENV_CONFIG:-?} (refreshing…)"',
|
|
395
|
+
'}',
|
|
321
396
|
// One-shot env auto-load when a NEW shell opens inside an infra-kit project
|
|
322
397
|
// or worktree (config: envAutoLoad.trigger "shell-startup"). Cheap pure-zsh
|
|
323
398
|
// project gate (walk up for infra-kit.json) avoids spawning node in unrelated
|
|
@@ -325,17 +400,24 @@ export const buildShellBody = (): string => {
|
|
|
325
400
|
// project root (INFRA_KIT_ENV_PROJECT_ROOT), so cd'ing into a DIFFERENT project
|
|
326
401
|
// re-loads instead of silently keeping the previous project's secrets. The spawn
|
|
327
402
|
// is DETACHED+backgrounded ( ... & ) so it never blocks the prompt; it only
|
|
328
|
-
// WRITES env-load.sh
|
|
329
|
-
// a
|
|
330
|
-
// a fresh shell in a project WITHOUT
|
|
331
|
-
// process that resolves config, finds
|
|
403
|
+
// WRITES env-load.sh. The startup poll armed right after (and the precmd hook as
|
|
404
|
+
// a later-prompt fallback) sources the file once it lands. Trade-off: the gate
|
|
405
|
+
// can't read the merged JSON config, so a fresh shell in a project WITHOUT
|
|
406
|
+
// envAutoLoad still spawns one background process that resolves config, finds
|
|
407
|
+
// nothing, and exits.
|
|
332
408
|
'_infra_kit_startup_autoload() {',
|
|
333
409
|
' [[ -z "$INFRA_KIT_SESSION" ]] && return',
|
|
334
410
|
' local dir="$PWD" prev=""',
|
|
335
411
|
' while [[ -n "$dir" && "$dir" != "$prev" ]]; do',
|
|
336
412
|
' if [[ -f "$dir/infra-kit.json" ]]; then',
|
|
337
413
|
' [[ -n "$INFRA_KIT_ENV_CONFIG" && "$dir" == "$INFRA_KIT_ENV_PROJECT_ROOT" ]] && return',
|
|
338
|
-
|
|
414
|
+
// canon = realpath of the project dir; the SAME string node hashes for the warm
|
|
415
|
+
// key (passed via --project-dir), so the two keys are byte-identical.
|
|
416
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
417
|
+
' local canon="${dir:A}"',
|
|
418
|
+
' _infra_kit_warm_source "$canon"',
|
|
419
|
+
` ( ${runCmd} env-autoload --project-dir "$canon" & ) >/dev/null 2>&1`,
|
|
420
|
+
' (( $+builtins[sched] )) && sched +1 _infra_kit_poll_autoload',
|
|
339
421
|
' return',
|
|
340
422
|
' fi',
|
|
341
423
|
' prev="$dir"',
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { toDevServerOptions } from 'src/entry/dev-server'
|
|
4
|
+
import type { DevCliOptions } from 'src/entry/dev-server'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Flag/option parsing for the dev-server CLI entry. `toDevServerOptions` maps the
|
|
8
|
+
* raw Commander flag object (comma-joined strings) to the typed `DevServerOptions`
|
|
9
|
+
* the orchestrator consumes. Pure function — no fixtures.
|
|
10
|
+
*/
|
|
11
|
+
describe('toDevServerOptions — CLI flag parsing', () => {
|
|
12
|
+
it('splits --app into an include list', () => {
|
|
13
|
+
const opts = toDevServerOptions({ app: 'client,backoffice' } satisfies DevCliOptions)
|
|
14
|
+
|
|
15
|
+
expect(opts.include).toEqual(['client', 'backoffice'])
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('trims whitespace and drops empty segments in comma lists', () => {
|
|
19
|
+
const opts = toDevServerOptions({ app: ' client , , backoffice ,' } satisfies DevCliOptions)
|
|
20
|
+
|
|
21
|
+
expect(opts.include).toEqual(['client', 'backoffice'])
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('maps a missing --app to null (means "all apps")', () => {
|
|
25
|
+
const opts = toDevServerOptions({} satisfies DevCliOptions)
|
|
26
|
+
|
|
27
|
+
expect(opts.include).toBeNull()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('maps an all-empty comma list to null rather than an empty array', () => {
|
|
31
|
+
const opts = toDevServerOptions({ app: ' , , ' } satisfies DevCliOptions)
|
|
32
|
+
|
|
33
|
+
expect(opts.include).toBeNull()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('defaults watch to false when the flag is absent', () => {
|
|
37
|
+
expect(toDevServerOptions({} satisfies DevCliOptions).watch).toBe(false)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('passes --watch through as true', () => {
|
|
41
|
+
expect(toDevServerOptions({ watch: true } satisfies DevCliOptions).watch).toBe(true)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('parses a combined flag set (--app --watch) in one pass', () => {
|
|
45
|
+
const opts = toDevServerOptions({ app: 'a,b', watch: true } satisfies DevCliOptions)
|
|
46
|
+
|
|
47
|
+
expect(opts).toEqual({ watch: true, include: ['a', 'b'], ui: false })
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('defaults ui to false when --ui is absent (api-only, backward compatible)', () => {
|
|
51
|
+
expect(toDevServerOptions({} satisfies DevCliOptions).ui).toBe(false)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('passes --ui through as true', () => {
|
|
55
|
+
expect(toDevServerOptions({ ui: true } satisfies DevCliOptions).ui).toBe(true)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import * as fs from 'node:fs'
|
|
2
|
+
import * as path from 'node:path'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
5
|
+
|
|
6
|
+
import { DevServerRunner } from 'src/dev/dev-server'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
canBind,
|
|
10
|
+
createTempTracker,
|
|
11
|
+
getFreePort,
|
|
12
|
+
handlerSource,
|
|
13
|
+
makeMonorepo,
|
|
14
|
+
restoreCwd,
|
|
15
|
+
restoreEnv,
|
|
16
|
+
snapshotCwd,
|
|
17
|
+
snapshotEnv,
|
|
18
|
+
spyStdoutWrite,
|
|
19
|
+
} from './fixtures'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Deterministic orchestration tier: a real monorepo fixture + the REAL ServerlessLocalRun
|
|
23
|
+
* spawn, but the turbo build is replaced with an injected fake (2nd constructor arg) that
|
|
24
|
+
* records its calls and refreshes each app's dist output. No chokidar, no watch, no timers
|
|
25
|
+
* — start() then shutdown() is the whole lifecycle under test.
|
|
26
|
+
*/
|
|
27
|
+
const temp = createTempTracker()
|
|
28
|
+
let envSnapshot: NodeJS.ProcessEnv
|
|
29
|
+
let cwdSnapshot: string
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
envSnapshot = snapshotEnv()
|
|
33
|
+
cwdSnapshot = snapshotCwd()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
restoreCwd(cwdSnapshot)
|
|
38
|
+
restoreEnv(envSnapshot)
|
|
39
|
+
temp.cleanup()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe('devServerRunner — start/shutdown lifecycle (fake build, real spawn)', () => {
|
|
43
|
+
it('boots each discovered app on its resolved port and serves /__health, then frees every port on shutdown', async () => {
|
|
44
|
+
const root = temp.register(
|
|
45
|
+
makeMonorepo([
|
|
46
|
+
{ name: 'alpha', packageName: 'alpha-api', withHandler: true },
|
|
47
|
+
{ name: 'beta', packageName: 'beta-api', withHandler: true },
|
|
48
|
+
]),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
// Distinct free ports via env keep the conflict pre-check happy and avoid collisions.
|
|
52
|
+
const alphaPort = await getFreePort()
|
|
53
|
+
const betaPort = await getFreePort()
|
|
54
|
+
|
|
55
|
+
process.env.ALPHA_PORT = String(alphaPort)
|
|
56
|
+
process.env.BETA_PORT = String(betaPort)
|
|
57
|
+
|
|
58
|
+
// Fake build: record the command and (re)write fresh dist output for each app.
|
|
59
|
+
const buildCalls: string[] = []
|
|
60
|
+
const apiDirs = ['alpha', 'beta'].map((name) => {
|
|
61
|
+
return path.join(root, 'apps', name, 'api')
|
|
62
|
+
})
|
|
63
|
+
const fakeRunBuild = async (cmd: string): Promise<void> => {
|
|
64
|
+
buildCalls.push(cmd)
|
|
65
|
+
for (const dir of apiDirs) {
|
|
66
|
+
fs.writeFileSync(path.join(dir, 'dist', 'handler.js'), handlerSource(1))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// The runner resolves the monorepo root from cwd, so run from the fixture root.
|
|
71
|
+
process.chdir(root)
|
|
72
|
+
const runner = new DevServerRunner({}, fakeRunBuild)
|
|
73
|
+
|
|
74
|
+
await runner.start()
|
|
75
|
+
|
|
76
|
+
// The build seam was invoked (the initial turbo build).
|
|
77
|
+
expect(buildCalls.length).toBeGreaterThan(0)
|
|
78
|
+
|
|
79
|
+
// Both real servers are listening and answer their health route with their own port.
|
|
80
|
+
const alphaHealth = (await (await fetch(`http://127.0.0.1:${alphaPort}/__health`)).json()) as {
|
|
81
|
+
app: string
|
|
82
|
+
port: number
|
|
83
|
+
}
|
|
84
|
+
const betaHealth = (await (await fetch(`http://127.0.0.1:${betaPort}/__health`)).json()) as {
|
|
85
|
+
app: string
|
|
86
|
+
port: number
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
expect(alphaHealth).toMatchObject({ app: 'alpha', port: alphaPort })
|
|
90
|
+
expect(betaHealth).toMatchObject({ app: 'beta', port: betaPort })
|
|
91
|
+
|
|
92
|
+
// Ports are held while running.
|
|
93
|
+
expect(await canBind(alphaPort)).toBe(false)
|
|
94
|
+
expect(await canBind(betaPort)).toBe(false)
|
|
95
|
+
|
|
96
|
+
await runner.shutdown()
|
|
97
|
+
|
|
98
|
+
// shutdown() releases every port so a fresh listener can bind again.
|
|
99
|
+
expect(await canBind(alphaPort)).toBe(true)
|
|
100
|
+
expect(await canBind(betaPort)).toBe(true)
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
describe('start — port-conflict pre-check', () => {
|
|
105
|
+
it('throws a clear error naming the port when two apps resolve to the same port', async () => {
|
|
106
|
+
const root = temp.register(makeMonorepo([{ name: 'client' }, { name: 'backoffice' }]))
|
|
107
|
+
|
|
108
|
+
// No per-app config and no env override => both resolve to 3010.
|
|
109
|
+
delete process.env.PORT
|
|
110
|
+
process.chdir(root)
|
|
111
|
+
const runner = new DevServerRunner({})
|
|
112
|
+
|
|
113
|
+
// The duplicate-port guard runs before any turbo build, so start() rejects
|
|
114
|
+
// hermetically without ever shelling out.
|
|
115
|
+
await expect(runner.start()).rejects.toThrow(/Port conflict detected: 3010/)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('does not throw the conflict error when apps resolve to distinct ports', async () => {
|
|
119
|
+
const root = temp.register(makeMonorepo([{ name: 'client' }, { name: 'backoffice' }]))
|
|
120
|
+
|
|
121
|
+
// Distinct per-app ports via env keep the pre-check happy; the build step then
|
|
122
|
+
// fails (no turbo in the fixture) — proving we got past the port guard.
|
|
123
|
+
process.env.CLIENT_PORT = '3010'
|
|
124
|
+
process.env.BACKOFFICE_PORT = '3011'
|
|
125
|
+
process.chdir(root)
|
|
126
|
+
const runner = new DevServerRunner({})
|
|
127
|
+
|
|
128
|
+
await expect(runner.start()).rejects.not.toThrow(/Port conflict/)
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
describe('start — --app selection + env-resolved port', () => {
|
|
133
|
+
it('runs only the --app subset (skipping the rest) and resolves its port from {APP}_PORT', async () => {
|
|
134
|
+
const root = temp.register(
|
|
135
|
+
makeMonorepo([
|
|
136
|
+
{ name: 'client', packageName: 'client-api', withHandler: true },
|
|
137
|
+
{ name: 'backoffice', packageName: 'backoffice-api', withHandler: true },
|
|
138
|
+
]),
|
|
139
|
+
)
|
|
140
|
+
const apiDirs = ['client', 'backoffice'].map((name) => {
|
|
141
|
+
return path.join(root, 'apps', name, 'api')
|
|
142
|
+
})
|
|
143
|
+
const fakeRunBuild = async (): Promise<void> => {
|
|
144
|
+
for (const dir of apiDirs) {
|
|
145
|
+
fs.writeFileSync(path.join(dir, 'dist', 'handler.js'), handlerSource(1))
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// `--app client` selects one app; its port comes purely from the CLIENT_PORT env var.
|
|
150
|
+
const clientPort = await getFreePort()
|
|
151
|
+
const backofficePort = await getFreePort()
|
|
152
|
+
|
|
153
|
+
delete process.env.PORT
|
|
154
|
+
process.env.CLIENT_PORT = String(clientPort)
|
|
155
|
+
process.env.BACKOFFICE_PORT = String(backofficePort)
|
|
156
|
+
process.chdir(root)
|
|
157
|
+
const runner = new DevServerRunner({ include: ['client'] }, fakeRunBuild)
|
|
158
|
+
|
|
159
|
+
await runner.start()
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
// Only `client` booted, on its {APP}_PORT-resolved port.
|
|
163
|
+
const health = (await (await fetch(`http://127.0.0.1:${clientPort}/__health`)).json()) as {
|
|
164
|
+
app: string
|
|
165
|
+
port: number
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
expect(health).toMatchObject({ app: 'client', port: clientPort })
|
|
169
|
+
|
|
170
|
+
// `backoffice` was filtered out by --app, so its port never gets bound.
|
|
171
|
+
expect(await canBind(backofficePort)).toBe(true)
|
|
172
|
+
} finally {
|
|
173
|
+
await runner.shutdown()
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
/** Boot a single-app monorepo fixture, capturing stdout across start(); returns the output + runner. */
|
|
179
|
+
const bootAndCaptureStdout = async (
|
|
180
|
+
temp: ReturnType<typeof createTempTracker>,
|
|
181
|
+
appName: string,
|
|
182
|
+
): Promise<{ out: string; runner: DevServerRunner }> => {
|
|
183
|
+
const root = temp.register(makeMonorepo([{ name: appName, packageName: `${appName}-api`, withHandler: true }]))
|
|
184
|
+
const port = await getFreePort()
|
|
185
|
+
|
|
186
|
+
process.env[`${appName.replace(/-/g, '_').toUpperCase()}_PORT`] = String(port)
|
|
187
|
+
|
|
188
|
+
const apiDir = path.join(root, 'apps', appName, 'api')
|
|
189
|
+
const fakeRunBuild = async (): Promise<void> => {
|
|
190
|
+
fs.writeFileSync(path.join(apiDir, 'dist', 'handler.js'), handlerSource(1))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const writes: string[] = []
|
|
194
|
+
const spy = spyStdoutWrite(writes)
|
|
195
|
+
|
|
196
|
+
process.chdir(root)
|
|
197
|
+
const runner = new DevServerRunner({}, fakeRunBuild)
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
await runner.start()
|
|
201
|
+
} finally {
|
|
202
|
+
spy.mockRestore()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return { out: writes.join(''), runner }
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
describe('devServerRunner — startup server table', () => {
|
|
209
|
+
it('renders truthful, aligned rows: prefix in the base URL, a /__health URL, equal-width lines', async () => {
|
|
210
|
+
// A long app name is exactly what broke the old fixed-width padEnd(24) table.
|
|
211
|
+
const longName = 'super-long-application-name'
|
|
212
|
+
const { out, runner } = await bootAndCaptureStdout(temp, longName)
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
// Truthful: the base URL carries the /api/v1 prefix and the health URL is shown.
|
|
216
|
+
expect(out).toContain('/api/v1')
|
|
217
|
+
expect(out).toContain('/__health')
|
|
218
|
+
expect(out).toContain(longName)
|
|
219
|
+
|
|
220
|
+
// Aligned: every box-drawn line shares one width (measured in UTF-16 units, as the
|
|
221
|
+
// renderer computes them — the title emoji is a surrogate pair, so code-point counts differ).
|
|
222
|
+
const tableLines = out.split('\n').filter((line) => {
|
|
223
|
+
return /^[┌│├└]/.test(line)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
expect(tableLines.length).toBeGreaterThan(0)
|
|
227
|
+
expect(
|
|
228
|
+
new Set(
|
|
229
|
+
tableLines.map((line) => {
|
|
230
|
+
return line.length
|
|
231
|
+
}),
|
|
232
|
+
).size,
|
|
233
|
+
).toBe(1)
|
|
234
|
+
} finally {
|
|
235
|
+
await runner.shutdown()
|
|
236
|
+
}
|
|
237
|
+
}, 15000)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
describe('devServerRunner — startup route dump', () => {
|
|
241
|
+
it('lists each app registered METHOD /path routes at startup', async () => {
|
|
242
|
+
const { out, runner } = await bootAndCaptureStdout(temp, 'epsilon')
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
expect(out).toContain('Registered routes')
|
|
246
|
+
expect(out).toContain('epsilon')
|
|
247
|
+
// The monorepo fixture declares a single GET /api/v1/ping route.
|
|
248
|
+
expect(out).toContain('GET /api/v1/ping')
|
|
249
|
+
} finally {
|
|
250
|
+
await runner.shutdown()
|
|
251
|
+
}
|
|
252
|
+
}, 15000)
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
describe('devServerRunner — watch mode (dist-watch, build-less restart)', () => {
|
|
256
|
+
it('restarts on a dist change without rebuilding, spawns the turbo engine, and reaps it on shutdown', async () => {
|
|
257
|
+
const root = temp.register(makeMonorepo([{ name: 'omega', packageName: 'omega-api', withHandler: true }]))
|
|
258
|
+
const realRoot = fs.realpathSync(root)
|
|
259
|
+
const apiDir = path.join(realRoot, 'apps', 'omega', 'api')
|
|
260
|
+
const distHandler = path.join(apiDir, 'dist', 'handler.js')
|
|
261
|
+
|
|
262
|
+
const omegaPort = await getFreePort()
|
|
263
|
+
|
|
264
|
+
process.env.OMEGA_PORT = String(omegaPort)
|
|
265
|
+
// Hermetic polling so the dist watcher fires without relying on native fs events.
|
|
266
|
+
process.env.DEV_SERVER_CHOKIDAR_POLL = '1'
|
|
267
|
+
|
|
268
|
+
// Fake build: boot build only. In turbo mode the on-change rebuild is owned by the
|
|
269
|
+
// (faked) turbo engine, so runBuild must NOT be called again after boot.
|
|
270
|
+
const buildCalls: string[] = []
|
|
271
|
+
const fakeRunBuild = async (cmd: string): Promise<void> => {
|
|
272
|
+
buildCalls.push(cmd)
|
|
273
|
+
fs.writeFileSync(distHandler, handlerSource(1))
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// No-op turbo engine: record that it was asked to start (+ with which packages) and
|
|
277
|
+
// that it was killed on shutdown, without spawning a real child.
|
|
278
|
+
let killed = false
|
|
279
|
+
let spawnedPackages: string[] | null = null
|
|
280
|
+
const fakeTurboWatch = (opts: { packageNames: string[] }): { kill: () => Promise<void> } => {
|
|
281
|
+
spawnedPackages = opts.packageNames
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
kill: (): Promise<void> => {
|
|
285
|
+
killed = true
|
|
286
|
+
|
|
287
|
+
return Promise.resolve()
|
|
288
|
+
},
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
process.chdir(root)
|
|
293
|
+
const runner = new DevServerRunner({ watch: true }, fakeRunBuild, fakeTurboWatch)
|
|
294
|
+
|
|
295
|
+
await runner.start()
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
const v1 = (await (await fetch(`http://127.0.0.1:${omegaPort}/api/v1/ping`)).json()) as { version: number }
|
|
299
|
+
|
|
300
|
+
expect(v1.version).toBe(1)
|
|
301
|
+
|
|
302
|
+
// The turbo engine was started, scoped to the app package.
|
|
303
|
+
expect(spawnedPackages).toEqual(['omega-api'])
|
|
304
|
+
|
|
305
|
+
// Exactly one build so far: the boot build. No rebuild happens on a dist change.
|
|
306
|
+
const buildsAfterBoot = buildCalls.length
|
|
307
|
+
|
|
308
|
+
expect(buildsAfterBoot).toBe(1)
|
|
309
|
+
|
|
310
|
+
// Let chokidar finish its initial scan, then simulate the turbo engine emitting a
|
|
311
|
+
// fresh dist build (v2) — the signal the dev-server restarts on.
|
|
312
|
+
await new Promise((r) => {
|
|
313
|
+
return setTimeout(r, 1200)
|
|
314
|
+
})
|
|
315
|
+
fs.writeFileSync(distHandler, handlerSource(2))
|
|
316
|
+
|
|
317
|
+
const deadline = Date.now() + 8000
|
|
318
|
+
let sawV2 = false
|
|
319
|
+
|
|
320
|
+
while (Date.now() < deadline && !sawV2) {
|
|
321
|
+
try {
|
|
322
|
+
const body = (await (await fetch(`http://127.0.0.1:${omegaPort}/api/v1/ping`)).json()) as { version: number }
|
|
323
|
+
|
|
324
|
+
if (body.version === 2) {
|
|
325
|
+
sawV2 = true
|
|
326
|
+
break
|
|
327
|
+
}
|
|
328
|
+
} catch {
|
|
329
|
+
// Server briefly down mid-restart; keep polling.
|
|
330
|
+
}
|
|
331
|
+
await new Promise((r) => {
|
|
332
|
+
return setTimeout(r, 100)
|
|
333
|
+
})
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Restarted and served the new dist on the same port…
|
|
337
|
+
expect(sawV2).toBe(true)
|
|
338
|
+
// …without any additional build (the restart is build-less in turbo mode).
|
|
339
|
+
expect(buildCalls).toHaveLength(buildsAfterBoot)
|
|
340
|
+
} finally {
|
|
341
|
+
await runner.shutdown()
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// shutdown() reaped the turbo engine and freed the port.
|
|
345
|
+
expect(killed).toBe(true)
|
|
346
|
+
expect(await canBind(omegaPort)).toBe(true)
|
|
347
|
+
}, 15000)
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
describe('devServerRunner — --ui (frontends via delegated turbo run dev)', () => {
|
|
351
|
+
it('warms UI deps, spawns the ui-dev child scoped to ui packages, and reaps it on shutdown', async () => {
|
|
352
|
+
const root = temp.register(
|
|
353
|
+
makeMonorepo([{ name: 'shop', packageName: 'shop-api', withHandler: true, ui: { packageName: 'shop-ui' } }]),
|
|
354
|
+
)
|
|
355
|
+
const apiDir = path.join(root, 'apps', 'shop', 'api')
|
|
356
|
+
const shopPort = await getFreePort()
|
|
357
|
+
|
|
358
|
+
process.env.SHOP_PORT = String(shopPort)
|
|
359
|
+
|
|
360
|
+
const buildCalls: string[] = []
|
|
361
|
+
const fakeRunBuild = async (cmd: string): Promise<void> => {
|
|
362
|
+
buildCalls.push(cmd)
|
|
363
|
+
fs.writeFileSync(path.join(apiDir, 'dist', 'handler.js'), handlerSource(1))
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// turbo watch isn't used without --watch; a no-op factory keeps the seam satisfied.
|
|
367
|
+
const noopTurboWatch = (): { kill: () => Promise<void> } => {
|
|
368
|
+
return {
|
|
369
|
+
kill: (): Promise<void> => {
|
|
370
|
+
return Promise.resolve()
|
|
371
|
+
},
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
let uiKilled = false
|
|
376
|
+
let uiPackages: string[] | null = null
|
|
377
|
+
let uiConcurrency = 0
|
|
378
|
+
const fakeUiDev = (opts: { packageNames: string[]; concurrency: number }): { kill: () => Promise<void> } => {
|
|
379
|
+
uiPackages = opts.packageNames
|
|
380
|
+
uiConcurrency = opts.concurrency
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
kill: (): Promise<void> => {
|
|
384
|
+
uiKilled = true
|
|
385
|
+
|
|
386
|
+
return Promise.resolve()
|
|
387
|
+
},
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
process.chdir(root)
|
|
392
|
+
const runner = new DevServerRunner({ ui: true }, fakeRunBuild, noopTurboWatch, fakeUiDev)
|
|
393
|
+
|
|
394
|
+
await runner.start()
|
|
395
|
+
|
|
396
|
+
try {
|
|
397
|
+
// The API app still boots and serves (api + ui run together).
|
|
398
|
+
const health = (await (await fetch(`http://127.0.0.1:${shopPort}/__health`)).json()) as { app: string }
|
|
399
|
+
|
|
400
|
+
expect(health.app).toBe('shop')
|
|
401
|
+
|
|
402
|
+
// The ui-dev child was spawned, scoped to the UI package only, with adequate concurrency.
|
|
403
|
+
expect(uiPackages).toEqual(['shop-ui'])
|
|
404
|
+
expect(uiConcurrency).toBeGreaterThanOrEqual(1)
|
|
405
|
+
|
|
406
|
+
// A UI dependency-closure warm build ran (deps-only `^...`, not the UI's own build).
|
|
407
|
+
expect(
|
|
408
|
+
buildCalls.some((c) => {
|
|
409
|
+
return c.includes('shop-ui^...')
|
|
410
|
+
}),
|
|
411
|
+
).toBe(true)
|
|
412
|
+
} finally {
|
|
413
|
+
await runner.shutdown()
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// shutdown() reaped the ui-dev child and freed the API port.
|
|
417
|
+
expect(uiKilled).toBe(true)
|
|
418
|
+
expect(await canBind(shopPort)).toBe(true)
|
|
419
|
+
}, 15000)
|
|
420
|
+
})
|