quidproquo-cli 0.1.19 → 0.1.21

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 (130) hide show
  1. package/README.md +1 -0
  2. package/lib/commonjs/cli/commandRegistry.js +6 -0
  3. package/lib/commonjs/cli/commandRegistry.js.map +1 -1
  4. package/lib/commonjs/commands/goDevApi.js +18 -5
  5. package/lib/commonjs/commands/goDevApi.js.map +1 -1
  6. package/lib/commonjs/commands/goDevWeb.js +10 -2
  7. package/lib/commonjs/commands/goDevWeb.js.map +1 -1
  8. package/lib/commonjs/commands/setup.d.ts +1 -0
  9. package/lib/commonjs/commands/setup.js +102 -0
  10. package/lib/commonjs/commands/setup.js.map +1 -0
  11. package/lib/commonjs/lib/args.d.ts +1 -0
  12. package/lib/commonjs/lib/args.js +11 -1
  13. package/lib/commonjs/lib/args.js.map +1 -1
  14. package/lib/commonjs/lib/keepOtherDevServersFlag.d.ts +1 -0
  15. package/lib/commonjs/lib/keepOtherDevServersFlag.js +11 -0
  16. package/lib/commonjs/lib/keepOtherDevServersFlag.js.map +1 -0
  17. package/lib/commonjs/lib/killStaleListeners.d.ts +14 -0
  18. package/lib/commonjs/lib/killStaleListeners.js +47 -20
  19. package/lib/commonjs/lib/killStaleListeners.js.map +1 -1
  20. package/lib/commonjs/lib/prompts.d.ts +2 -0
  21. package/lib/commonjs/lib/prompts.js +1 -1
  22. package/lib/commonjs/lib/prompts.js.map +1 -1
  23. package/lib/commonjs/lib/setupStep.d.ts +16 -0
  24. package/lib/commonjs/lib/setupStep.js +3 -0
  25. package/lib/commonjs/lib/setupStep.js.map +1 -0
  26. package/lib/commonjs/platforms/aws/index.js +2 -0
  27. package/lib/commonjs/platforms/aws/index.js.map +1 -1
  28. package/lib/commonjs/platforms/aws/setup/awsSetupContext.d.ts +12 -0
  29. package/lib/commonjs/platforms/aws/setup/awsSetupContext.js +23 -0
  30. package/lib/commonjs/platforms/aws/setup/awsSetupContext.js.map +1 -0
  31. package/lib/commonjs/platforms/aws/setup/awsSetupSteps.d.ts +8 -0
  32. package/lib/commonjs/platforms/aws/setup/awsSetupSteps.js +44 -0
  33. package/lib/commonjs/platforms/aws/setup/awsSetupSteps.js.map +1 -0
  34. package/lib/commonjs/platforms/aws/setup/buildBucketNamesStep.d.ts +4 -0
  35. package/lib/commonjs/platforms/aws/setup/buildBucketNamesStep.js +77 -0
  36. package/lib/commonjs/platforms/aws/setup/buildBucketNamesStep.js.map +1 -0
  37. package/lib/commonjs/platforms/aws/setup/buildCdkBootstrapSteps.d.ts +5 -0
  38. package/lib/commonjs/platforms/aws/setup/buildCdkBootstrapSteps.js +38 -0
  39. package/lib/commonjs/platforms/aws/setup/buildCdkBootstrapSteps.js.map +1 -0
  40. package/lib/commonjs/platforms/aws/setup/buildGithubDeployRoleStep.d.ts +4 -0
  41. package/lib/commonjs/platforms/aws/setup/buildGithubDeployRoleStep.js +55 -0
  42. package/lib/commonjs/platforms/aws/setup/buildGithubDeployRoleStep.js.map +1 -0
  43. package/lib/commonjs/platforms/aws/setup/buildGithubOidcProviderStep.d.ts +4 -0
  44. package/lib/commonjs/platforms/aws/setup/buildGithubOidcProviderStep.js +54 -0
  45. package/lib/commonjs/platforms/aws/setup/buildGithubOidcProviderStep.js.map +1 -0
  46. package/lib/commonjs/platforms/aws/setup/buildHostedZonesStep.d.ts +4 -0
  47. package/lib/commonjs/platforms/aws/setup/buildHostedZonesStep.js +63 -0
  48. package/lib/commonjs/platforms/aws/setup/buildHostedZonesStep.js.map +1 -0
  49. package/lib/commonjs/platforms/aws/setup/buildIamCapabilityStep.d.ts +4 -0
  50. package/lib/commonjs/platforms/aws/setup/buildIamCapabilityStep.js +48 -0
  51. package/lib/commonjs/platforms/aws/setup/buildIamCapabilityStep.js.map +1 -0
  52. package/lib/commonjs/platforms/aws/setup/buildIdentityStep.d.ts +4 -0
  53. package/lib/commonjs/platforms/aws/setup/buildIdentityStep.js +39 -0
  54. package/lib/commonjs/platforms/aws/setup/buildIdentityStep.js.map +1 -0
  55. package/lib/commonjs/platforms/aws/setup/buildStackSteps.d.ts +5 -0
  56. package/lib/commonjs/platforms/aws/setup/buildStackSteps.js +32 -0
  57. package/lib/commonjs/platforms/aws/setup/buildStackSteps.js.map +1 -0
  58. package/lib/commonjs/platforms/aws/setup/describeStackStatus.d.ts +4 -0
  59. package/lib/commonjs/platforms/aws/setup/describeStackStatus.js +32 -0
  60. package/lib/commonjs/platforms/aws/setup/describeStackStatus.js.map +1 -0
  61. package/lib/commonjs/platforms/aws/setup/index.d.ts +1 -0
  62. package/lib/commonjs/platforms/aws/setup/index.js +18 -0
  63. package/lib/commonjs/platforms/aws/setup/index.js.map +1 -0
  64. package/lib/commonjs/platforms/types.d.ts +2 -0
  65. package/lib/commonjs/platforms/types.js.map +1 -1
  66. package/lib/esm/cli/commandRegistry.js +6 -0
  67. package/lib/esm/cli/commandRegistry.js.map +1 -1
  68. package/lib/esm/commands/goDevApi.js +19 -6
  69. package/lib/esm/commands/goDevApi.js.map +1 -1
  70. package/lib/esm/commands/goDevWeb.js +10 -2
  71. package/lib/esm/commands/goDevWeb.js.map +1 -1
  72. package/lib/esm/commands/setup.d.ts +1 -0
  73. package/lib/esm/commands/setup.js +88 -0
  74. package/lib/esm/commands/setup.js.map +1 -0
  75. package/lib/esm/lib/args.d.ts +1 -0
  76. package/lib/esm/lib/args.js +9 -0
  77. package/lib/esm/lib/args.js.map +1 -1
  78. package/lib/esm/lib/keepOtherDevServersFlag.d.ts +1 -0
  79. package/lib/esm/lib/keepOtherDevServersFlag.js +8 -0
  80. package/lib/esm/lib/keepOtherDevServersFlag.js.map +1 -0
  81. package/lib/esm/lib/killStaleListeners.d.ts +14 -0
  82. package/lib/esm/lib/killStaleListeners.js +45 -19
  83. package/lib/esm/lib/killStaleListeners.js.map +1 -1
  84. package/lib/esm/lib/prompts.d.ts +2 -0
  85. package/lib/esm/lib/prompts.js +1 -1
  86. package/lib/esm/lib/prompts.js.map +1 -1
  87. package/lib/esm/lib/setupStep.d.ts +16 -0
  88. package/lib/esm/lib/setupStep.js +2 -0
  89. package/lib/esm/lib/setupStep.js.map +1 -0
  90. package/lib/esm/platforms/aws/index.js +2 -0
  91. package/lib/esm/platforms/aws/index.js.map +1 -1
  92. package/lib/esm/platforms/aws/setup/awsSetupContext.d.ts +12 -0
  93. package/lib/esm/platforms/aws/setup/awsSetupContext.js +18 -0
  94. package/lib/esm/platforms/aws/setup/awsSetupContext.js.map +1 -0
  95. package/lib/esm/platforms/aws/setup/awsSetupSteps.d.ts +8 -0
  96. package/lib/esm/platforms/aws/setup/awsSetupSteps.js +31 -0
  97. package/lib/esm/platforms/aws/setup/awsSetupSteps.js.map +1 -0
  98. package/lib/esm/platforms/aws/setup/buildBucketNamesStep.d.ts +4 -0
  99. package/lib/esm/platforms/aws/setup/buildBucketNamesStep.js +62 -0
  100. package/lib/esm/platforms/aws/setup/buildBucketNamesStep.js.map +1 -0
  101. package/lib/esm/platforms/aws/setup/buildCdkBootstrapSteps.d.ts +5 -0
  102. package/lib/esm/platforms/aws/setup/buildCdkBootstrapSteps.js +24 -0
  103. package/lib/esm/platforms/aws/setup/buildCdkBootstrapSteps.js.map +1 -0
  104. package/lib/esm/platforms/aws/setup/buildGithubDeployRoleStep.d.ts +4 -0
  105. package/lib/esm/platforms/aws/setup/buildGithubDeployRoleStep.js +40 -0
  106. package/lib/esm/platforms/aws/setup/buildGithubDeployRoleStep.js.map +1 -0
  107. package/lib/esm/platforms/aws/setup/buildGithubOidcProviderStep.d.ts +4 -0
  108. package/lib/esm/platforms/aws/setup/buildGithubOidcProviderStep.js +40 -0
  109. package/lib/esm/platforms/aws/setup/buildGithubOidcProviderStep.js.map +1 -0
  110. package/lib/esm/platforms/aws/setup/buildHostedZonesStep.d.ts +4 -0
  111. package/lib/esm/platforms/aws/setup/buildHostedZonesStep.js +49 -0
  112. package/lib/esm/platforms/aws/setup/buildHostedZonesStep.js.map +1 -0
  113. package/lib/esm/platforms/aws/setup/buildIamCapabilityStep.d.ts +4 -0
  114. package/lib/esm/platforms/aws/setup/buildIamCapabilityStep.js +34 -0
  115. package/lib/esm/platforms/aws/setup/buildIamCapabilityStep.js.map +1 -0
  116. package/lib/esm/platforms/aws/setup/buildIdentityStep.d.ts +4 -0
  117. package/lib/esm/platforms/aws/setup/buildIdentityStep.js +26 -0
  118. package/lib/esm/platforms/aws/setup/buildIdentityStep.js.map +1 -0
  119. package/lib/esm/platforms/aws/setup/buildStackSteps.d.ts +5 -0
  120. package/lib/esm/platforms/aws/setup/buildStackSteps.js +17 -0
  121. package/lib/esm/platforms/aws/setup/buildStackSteps.js.map +1 -0
  122. package/lib/esm/platforms/aws/setup/describeStackStatus.d.ts +4 -0
  123. package/lib/esm/platforms/aws/setup/describeStackStatus.js +17 -0
  124. package/lib/esm/platforms/aws/setup/describeStackStatus.js.map +1 -0
  125. package/lib/esm/platforms/aws/setup/index.d.ts +1 -0
  126. package/lib/esm/platforms/aws/setup/index.js +2 -0
  127. package/lib/esm/platforms/aws/setup/index.js.map +1 -0
  128. package/lib/esm/platforms/types.d.ts +2 -0
  129. package/lib/esm/platforms/types.js.map +1 -1
  130. package/package.json +13 -10
package/README.md CHANGED
@@ -26,6 +26,7 @@ Running `qpq` with no command opens an interactive menu. Every command also take
26
26
 
27
27
  | Command | Does |
28
28
  | --- | --- |
29
+ | `qpq setup` | Prepares a cloud environment for its first deploy: checks every step (credentials, IAM, hosted zones, bucket names, CDK bootstrap, then the account, domain and bootstrap stacks with the GitHub OIDC provider and deploy role they create), shows what is already in place, and runs the ones you tick in order. Services are deployed afterwards with `qpq go` or `qpq go:docker`. `--check` only reports, `--yes` runs everything, `--only <ids>` a subset |
29
30
  | `qpq go [svc] [stack]` | Deploys services. Interactive unless you pass the positional args |
30
31
  | `qpq go:docker` | The same, but deploys in parallel via docker |
31
32
  | `qpq synth [service]` | Synthesises the configs to `dist/apps/<app>/infrastructure` without deploying |
@@ -37,6 +37,12 @@ exports.cliCommands = [
37
37
  ],
38
38
  run: (a) => require('../commands/go').goCommand(a),
39
39
  },
40
+ {
41
+ name: 'setup',
42
+ summary: 'Take a cloud environment from empty to deployed: checked steps, run in order (interactive)',
43
+ usageExtra: ["--check prints each step's status; --yes runs them all; --only <ids> runs a subset."],
44
+ run: (a) => require('../commands/setup').setupCommand(a),
45
+ },
40
46
  {
41
47
  name: 'go:docker',
42
48
  summary: 'Same as go (incl. positional args), but deploys in parallel via docker',
@@ -1 +1 @@
1
- {"version":3,"file":"commandRegistry.js","sourceRoot":"","sources":["../../../src/cli/commandRegistry.ts"],"names":[],"mappings":"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","sourcesContent":["// Single source of truth for every qpq command: its name, the one-line\n// summary (shared by `qpq help` and the interactive menu) and a runner.\n//\n// Command modules are required lazily inside each runner so a simple\n// `qpq help` doesn't pay for the whole toolchain, and so ts-node can be\n// registered before any command module (or anything it requires) loads.\nimport { promptText } from '../lib/prompts';\n\nexport type CliCommand = {\n name: string;\n // Argument hint shown in the usage text, e.g. '[svc] [stack]'.\n usageArgs?: string;\n summary: string;\n // Extra usage-only detail lines, indented under the summary.\n usageExtra?: string[];\n // Menu-only: commands that need a positional argument (which the menu has\n // no argv to carry) gather it here before running.\n promptArgs?: () => Promise<string[]>;\n run: (commandArgv: string[]) => Promise<void>;\n};\n\n// `qpq hooks` needs a hook name; when launched from the menu there is no\n// argv to pass it in, so ask.\nconst promptHookName = async (): Promise<string[]> => [\n await promptText('Hook name (runs qpq:<name>, or plain <name>, in every app/package that defines it):'),\n];\n\n/* eslint-disable @typescript-eslint/no-require-imports */\nexport const cliCommands: CliCommand[] = [\n {\n name: 'go',\n usageArgs: '[svc] [stack]',\n summary: 'Deploy services (interactive; platform from deploy.config.json, default aws).',\n usageExtra: [\n 'Pass positional args to skip prompts, e.g. qpq go all all — svc is',\n \"'all' | comma-list | account | domain | bootstrap; stack is all|inf|api|web|views.\",\n \"A full 'all all' (or 'all api') deploy also publishes federated backends.\",\n ],\n run: (a) => require('../commands/go').goCommand(a),\n },\n {\n name: 'go:docker',\n summary: 'Same as go (incl. positional args), but deploys in parallel via docker',\n run: (a) => require('../commands/goDocker').goDockerCommand(a),\n },\n {\n name: 'go:dev',\n summary: 'Run the full local dev stack (api + web) in one process',\n run: (a) => require('../commands/goDev').goDevCommand(a),\n },\n {\n name: 'go:dev:api',\n summary: 'Run the local API dev server (hot reload)',\n run: (a) => require('../commands/goDevApi').goDevApiCommand(a),\n },\n {\n name: 'go:dev:web',\n summary: 'Run every views microfrontend dev server',\n run: (a) => require('../commands/goDevWeb').goDevWebCommand(a),\n },\n {\n name: 'migrate',\n usageArgs: '[app]',\n summary: 'Run pending migrations against the LOCAL dev store, once, then exit.',\n usageExtra: [\n 'Deployed, migrations run on a stack update; nothing local ever deploys, so',\n 'this is how you exercise one before shipping it. Records what it runs, so a',\n 'second call is a no-op.',\n ],\n run: (a) => require('../commands/migrate').migrateCommand(a),\n },\n {\n name: 'teardown',\n summary: 'Destroy web/api/inf stacks for selected services (interactive)',\n run: (a) => require('../commands/teardown').teardownCommand(a),\n },\n {\n name: 'clear-resources',\n summary: 'Empty selected buckets/tables (data only, stacks untouched; interactive)',\n run: (a) => require('../commands/clearResources').clearResourcesCommand(a),\n },\n {\n name: 'synth',\n usageArgs: '[service]',\n summary: 'Synth QPQ configs to dist/apps/<app>/infrastructure',\n run: (a) => require('../commands/synth').synthCommand(a),\n },\n {\n name: 'prep',\n summary: 'Regenerate apps/<app>/tsconfig.federated.json',\n run: (a) => require('../commands/prep').prepCommand(a),\n },\n {\n name: 'publish',\n summary: 'Build + upload + deploy federated remotes',\n run: (a) => require('../commands/publish').publishCommand(a),\n },\n {\n name: 'publish:build',\n summary: 'Build federated remotes locally',\n run: (a) => require('../commands/publish').publishBuildCommand(a),\n },\n {\n name: 'publish:upload',\n summary: 'Upload built version dirs (nothing goes live)',\n run: (a) => require('../commands/publish').publishUploadCommand(a),\n },\n {\n name: 'publish:deploy',\n summary: 'Flip manifests to the uploaded versions',\n run: (a) => require('../commands/publish').publishDeployCommand(a),\n },\n {\n name: 'check:circular',\n summary: 'Scan workspace sources for circular imports (fails on cycles; --warn to only report)',\n run: (a) => require('../commands/checkCircular').checkCircularCommand(a),\n },\n {\n name: 'hooks',\n usageArgs: '<name>',\n summary: 'Run qpq:<name> (or plain <name>) in every app/package that defines it (dependency ordered, parallel; --jobs=N)',\n promptArgs: promptHookName,\n run: (a) => require('../commands/hooks').hooksCommand(a),\n },\n];\n/* eslint-enable @typescript-eslint/no-require-imports */\n\n// Width of the \"name [args]\" column in the usage text.\nconst usageColumnWidth = 17;\n\nexport const buildUsage = (): string => {\n const commandLines = cliCommands.flatMap((command) => {\n const nameAndArgs = command.usageArgs ? `${command.name} ${command.usageArgs}` : command.name;\n const extraLines = (command.usageExtra ?? []).map((line) => ` ${''.padEnd(usageColumnWidth)}${line}`);\n return [` ${nameAndArgs.padEnd(usageColumnWidth)}${command.summary}`, ...extraLines];\n });\n\n return [\n 'qpq — build, dev and deploy orchestration for QPQ apps',\n '',\n 'Usage: qpq <command> [--app <name>] [--env <name>] [--platform <name>]',\n ' qpq (no command opens an interactive menu)',\n '',\n 'Commands:',\n ...commandLines,\n '',\n ].join('\\n');\n};\n"]}
1
+ {"version":3,"file":"commandRegistry.js","sourceRoot":"","sources":["../../../src/cli/commandRegistry.ts"],"names":[],"mappings":"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","sourcesContent":["// Single source of truth for every qpq command: its name, the one-line\n// summary (shared by `qpq help` and the interactive menu) and a runner.\n//\n// Command modules are required lazily inside each runner so a simple\n// `qpq help` doesn't pay for the whole toolchain, and so ts-node can be\n// registered before any command module (or anything it requires) loads.\nimport { promptText } from '../lib/prompts';\n\nexport type CliCommand = {\n name: string;\n // Argument hint shown in the usage text, e.g. '[svc] [stack]'.\n usageArgs?: string;\n summary: string;\n // Extra usage-only detail lines, indented under the summary.\n usageExtra?: string[];\n // Menu-only: commands that need a positional argument (which the menu has\n // no argv to carry) gather it here before running.\n promptArgs?: () => Promise<string[]>;\n run: (commandArgv: string[]) => Promise<void>;\n};\n\n// `qpq hooks` needs a hook name; when launched from the menu there is no\n// argv to pass it in, so ask.\nconst promptHookName = async (): Promise<string[]> => [\n await promptText('Hook name (runs qpq:<name>, or plain <name>, in every app/package that defines it):'),\n];\n\n/* eslint-disable @typescript-eslint/no-require-imports */\nexport const cliCommands: CliCommand[] = [\n {\n name: 'go',\n usageArgs: '[svc] [stack]',\n summary: 'Deploy services (interactive; platform from deploy.config.json, default aws).',\n usageExtra: [\n 'Pass positional args to skip prompts, e.g. qpq go all all — svc is',\n \"'all' | comma-list | account | domain | bootstrap; stack is all|inf|api|web|views.\",\n \"A full 'all all' (or 'all api') deploy also publishes federated backends.\",\n ],\n run: (a) => require('../commands/go').goCommand(a),\n },\n {\n name: 'setup',\n summary: 'Take a cloud environment from empty to deployed: checked steps, run in order (interactive)',\n usageExtra: [\"--check prints each step's status; --yes runs them all; --only <ids> runs a subset.\"],\n run: (a) => require('../commands/setup').setupCommand(a),\n },\n {\n name: 'go:docker',\n summary: 'Same as go (incl. positional args), but deploys in parallel via docker',\n run: (a) => require('../commands/goDocker').goDockerCommand(a),\n },\n {\n name: 'go:dev',\n summary: 'Run the full local dev stack (api + web) in one process',\n run: (a) => require('../commands/goDev').goDevCommand(a),\n },\n {\n name: 'go:dev:api',\n summary: 'Run the local API dev server (hot reload)',\n run: (a) => require('../commands/goDevApi').goDevApiCommand(a),\n },\n {\n name: 'go:dev:web',\n summary: 'Run every views microfrontend dev server',\n run: (a) => require('../commands/goDevWeb').goDevWebCommand(a),\n },\n {\n name: 'migrate',\n usageArgs: '[app]',\n summary: 'Run pending migrations against the LOCAL dev store, once, then exit.',\n usageExtra: [\n 'Deployed, migrations run on a stack update; nothing local ever deploys, so',\n 'this is how you exercise one before shipping it. Records what it runs, so a',\n 'second call is a no-op.',\n ],\n run: (a) => require('../commands/migrate').migrateCommand(a),\n },\n {\n name: 'teardown',\n summary: 'Destroy web/api/inf stacks for selected services (interactive)',\n run: (a) => require('../commands/teardown').teardownCommand(a),\n },\n {\n name: 'clear-resources',\n summary: 'Empty selected buckets/tables (data only, stacks untouched; interactive)',\n run: (a) => require('../commands/clearResources').clearResourcesCommand(a),\n },\n {\n name: 'synth',\n usageArgs: '[service]',\n summary: 'Synth QPQ configs to dist/apps/<app>/infrastructure',\n run: (a) => require('../commands/synth').synthCommand(a),\n },\n {\n name: 'prep',\n summary: 'Regenerate apps/<app>/tsconfig.federated.json',\n run: (a) => require('../commands/prep').prepCommand(a),\n },\n {\n name: 'publish',\n summary: 'Build + upload + deploy federated remotes',\n run: (a) => require('../commands/publish').publishCommand(a),\n },\n {\n name: 'publish:build',\n summary: 'Build federated remotes locally',\n run: (a) => require('../commands/publish').publishBuildCommand(a),\n },\n {\n name: 'publish:upload',\n summary: 'Upload built version dirs (nothing goes live)',\n run: (a) => require('../commands/publish').publishUploadCommand(a),\n },\n {\n name: 'publish:deploy',\n summary: 'Flip manifests to the uploaded versions',\n run: (a) => require('../commands/publish').publishDeployCommand(a),\n },\n {\n name: 'check:circular',\n summary: 'Scan workspace sources for circular imports (fails on cycles; --warn to only report)',\n run: (a) => require('../commands/checkCircular').checkCircularCommand(a),\n },\n {\n name: 'hooks',\n usageArgs: '<name>',\n summary: 'Run qpq:<name> (or plain <name>) in every app/package that defines it (dependency ordered, parallel; --jobs=N)',\n promptArgs: promptHookName,\n run: (a) => require('../commands/hooks').hooksCommand(a),\n },\n];\n/* eslint-enable @typescript-eslint/no-require-imports */\n\n// Width of the \"name [args]\" column in the usage text.\nconst usageColumnWidth = 17;\n\nexport const buildUsage = (): string => {\n const commandLines = cliCommands.flatMap((command) => {\n const nameAndArgs = command.usageArgs ? `${command.name} ${command.usageArgs}` : command.name;\n const extraLines = (command.usageExtra ?? []).map((line) => ` ${''.padEnd(usageColumnWidth)}${line}`);\n return [` ${nameAndArgs.padEnd(usageColumnWidth)}${command.summary}`, ...extraLines];\n });\n\n return [\n 'qpq — build, dev and deploy orchestration for QPQ apps',\n '',\n 'Usage: qpq <command> [--app <name>] [--env <name>] [--platform <name>]',\n ' qpq (no command opens an interactive menu)',\n '',\n 'Commands:',\n ...commandLines,\n '',\n ].join('\\n');\n};\n"]}
@@ -29,9 +29,11 @@ const quidproquo_deploy_rspack_1 = require("quidproquo-deploy-rspack");
29
29
  const child_process_1 = require("child_process");
30
30
  const path_1 = __importDefault(require("path"));
31
31
  const core_1 = require("@rspack/core");
32
+ const args_1 = require("../lib/args");
32
33
  const deployEnv_1 = require("../lib/deployEnv");
33
34
  const devServerEntry_1 = require("../lib/devServerEntry");
34
35
  const discovery_1 = require("../lib/discovery");
36
+ const keepOtherDevServersFlag_1 = require("../lib/keepOtherDevServersFlag");
35
37
  const killChildWithEscalation_1 = require("../lib/killChildWithEscalation");
36
38
  const killStaleListeners_1 = require("../lib/killStaleListeners");
37
39
  const resolveAppSelection_1 = require("../lib/resolveAppSelection");
@@ -45,11 +47,22 @@ const goDevApiCommand = (argv) => __awaiter(void 0, void 0, void 0, function* ()
45
47
  process.env.QPQ_DEV_APP = appName;
46
48
  (0, deployEnv_1.primeDeployEnvFromConfig)(appName);
47
49
  console.log(`Dev server for app [${appName}]`);
48
- // Catches a lingering watcher from a previous run even if its spawned
49
- // child already exited (see killOtherQpqDevProcesses) then the usual
50
- // port-based sweep for anything else still bound to our ports.
51
- (0, killStaleListeners_1.killOtherQpqDevProcesses)(root);
52
- (0, killStaleListeners_1.killStaleListeners)(DEV_SERVER_PORTS, (command) => command.includes(DEV_SERVER_BUNDLE_PATH));
50
+ // The sweep matches on a RELATIVE bundle path, so every checkout's dev
51
+ // server looks like ours and a sibling worktree gets taken down with it.
52
+ // Opting out spares it, at the cost of this one not starting: the ports are
53
+ // the same either way, so what you get instead is EADDRINUSE. Report who
54
+ // holds them, or that failure says nothing about the cause.
55
+ if ((0, args_1.hasArgFlag)(argv, keepOtherDevServersFlag_1.KEEP_OTHER_DEV_SERVERS_FLAG)) {
56
+ console.log(`${keepOtherDevServersFlag_1.KEEP_OTHER_DEV_SERVERS_FLAG}: leaving other dev servers alone.`);
57
+ (0, killStaleListeners_1.reportPortHolders)(DEV_SERVER_PORTS);
58
+ }
59
+ else {
60
+ // Catches a lingering watcher from a previous run even if its spawned
61
+ // child already exited (see killOtherQpqDevProcesses) — then the usual
62
+ // port-based sweep for anything else still bound to our ports.
63
+ (0, killStaleListeners_1.killOtherQpqDevProcesses)(root);
64
+ (0, killStaleListeners_1.killStaleListeners)(DEV_SERVER_PORTS, (command) => command.includes(DEV_SERVER_BUNDLE_PATH));
65
+ }
53
66
  const qpqConfigs = (0, quidproquo_deploy_rspack_1.getAppServiceQpqConfigs)(root, appName);
54
67
  const entry = (0, devServerEntry_1.writeDevServerEntry)(root, appName);
55
68
  const rspackConfig = (0, quidproquo_deploy_rspack_1.getDevServerRspackConfig)({ root, entry, qpqConfigs });
@@ -1 +1 @@
1
- {"version":3,"file":"goDevApi.js","sourceRoot":"","sources":["../../../src/commands/goDevApi.ts"],"names":[],"mappings":"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","sourcesContent":["// `qpq go:dev:api` — boots the QPQ local dev server. Resolves the app,\n// generates the dev-server entry, builds the rspack config, then runs rspack\n// in watch mode, restarting the server process on every successful rebuild.\n//\n// A restart is not instant: the server drains in-flight work and checkpoints\n// its stores on the way out (typically tens of ms, 5s worst case), and this\n// command waits for that before starting the replacement.\n//\n// The one thing NOT hot-reloaded is the qpq configs themselves: each service's\n// infrastructure.ts is evaluated once at launch and baked into the\n// `quidproquo-dynamic-loader` virtual module — config changes need a full\n// `go:dev:api` restart.\nimport { getAppServiceQpqConfigs, getDevServerRspackConfig } from 'quidproquo-deploy-rspack';\n\nimport { ChildProcess, spawn } from 'child_process';\nimport fs from 'fs';\nimport path from 'path';\nimport { rspack } from '@rspack/core';\n\nimport { primeDeployEnvFromConfig } from '../lib/deployEnv';\nimport { writeDevServerEntry } from '../lib/devServerEntry';\nimport { getRoot } from '../lib/discovery';\nimport { killChildWithEscalation } from '../lib/killChildWithEscalation';\nimport { killOtherQpqDevProcesses, killStaleListeners } from '../lib/killStaleListeners';\nimport { resolveAppSelection } from '../lib/resolveAppSelection';\n\n// 8080/8888 are set in the generated entry; 3001 is the quidproquo-dev-server\n// file-storage (secure URL) default port.\nconst DEV_SERVER_PORTS = [8080, 8888, 3001];\nconst DEV_SERVER_BUNDLE_PATH = path.join('dist', 'qpq', 'dev-server', 'main.js');\n\nexport const goDevApiCommand = async (argv: string[]): Promise<void> => {\n const root = getRoot();\n const appName = await resolveAppSelection({ argv, envVar: 'QPQ_DEV_APP' });\n process.env.QPQ_DEV_APP = appName;\n primeDeployEnvFromConfig(appName);\n console.log(`Dev server for app [${appName}]`);\n\n // Catches a lingering watcher from a previous run even if its spawned\n // child already exited (see killOtherQpqDevProcesses) — then the usual\n // port-based sweep for anything else still bound to our ports.\n killOtherQpqDevProcesses(root);\n killStaleListeners(DEV_SERVER_PORTS, (command) => command.includes(DEV_SERVER_BUNDLE_PATH));\n\n const qpqConfigs = getAppServiceQpqConfigs(root, appName);\n const entry = writeDevServerEntry(root, appName);\n const rspackConfig = getDevServerRspackConfig({ root, entry, qpqConfigs });\n const bundlePath = path.join(root, DEV_SERVER_BUNDLE_PATH);\n const env = { ...process.env, QPQ_DEV_APP: appName };\n\n // The long-lived server process (HTTP 8080 / WS 8888), restarted per rebuild.\n let child: ChildProcess | null = null;\n let restartQueued = false;\n\n const startServer = (): void => {\n child = spawn('node', [bundlePath], { stdio: 'inherit', env });\n child.on('exit', (code) => {\n child = null;\n if (!restartQueued) {\n // Crashed (or exited) on its own — leave it down; the next successful\n // rebuild (i.e. the next file save) brings it back up.\n console.log(`Dev server exited with code ${code}. Save a file to restart it.`);\n }\n });\n };\n\n const restartServer = (): void => {\n if (restartQueued) return;\n if (!child) {\n startServer();\n return;\n }\n restartQueued = true;\n child.once('exit', () => {\n restartQueued = false;\n startServer();\n });\n\n // Not a bare kill: the server drains in-flight work before it exits, so it\n // needs asking properly and then a hard backstop if it wedges. Waiting for\n // the exit is what keeps the ports free for the replacement.\n void killChildWithEscalation(child);\n };\n\n console.log('Bundling dev server (watch mode)...');\n let lastHash: string | null | undefined;\n const compiler = rspack(rspackConfig);\n compiler.watch({ aggregateTimeout: 200 }, (err, stats) => {\n if (err) {\n console.error(err);\n return;\n }\n if (stats?.hasErrors()) {\n console.error(stats.toString({ colors: true, chunks: false, modules: false }));\n console.log('Build failed — dev server not restarted. Fix the error and save again.');\n return;\n }\n // Skip no-op rebuilds (e.g. the virtual-module-triggered second compile at\n // startup): only restart when the output actually changed.\n if (stats?.hash === lastHash && child) {\n return;\n }\n lastHash = stats?.hash;\n console.log(child ? 'Rebuilt. Restarting dev server...' : 'Dev server bundled. Starting...');\n restartServer();\n });\n\n // Wait for the server to finish draining before tearing the watcher down and\n // exiting: closing the compiler first would exit the parent while the child\n // is still writing, which is the race this whole path exists to close.\n //\n // SIGTERM as well as SIGINT, and it is load-bearing rather than symmetry for\n // its own sake. This process is a WRAPPER: the dev server that handles\n // signals and drains its work is the child it spawned. Without a handler\n // here, SIGTERM (what `docker stop` sends, and what any script stopping this\n // programmatically sends) kills the wrapper outright on the default\n // disposition, the child never gets asked to stop, and every guarantee the\n // dev server's shutdown sequence makes is bypassed. go:dev:web already\n // handles both.\n let shuttingDown = false;\n\n const handleShutdownSignal = async (): Promise<void> => {\n if (shuttingDown) {\n return;\n }\n shuttingDown = true;\n\n restartQueued = true; // suppress the crash log / rebuild-restart on our own kill\n\n if (child) {\n await killChildWithEscalation(child);\n }\n\n compiler.close(() => process.exit(0));\n };\n\n process.on('SIGINT', () => {\n void handleShutdownSignal();\n });\n\n process.on('SIGTERM', () => {\n void handleShutdownSignal();\n });\n};\n"]}
1
+ {"version":3,"file":"goDevApi.js","sourceRoot":"","sources":["../../../src/commands/goDevApi.ts"],"names":[],"mappings":"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","sourcesContent":["// `qpq go:dev:api` — boots the QPQ local dev server. Resolves the app,\n// generates the dev-server entry, builds the rspack config, then runs rspack\n// in watch mode, restarting the server process on every successful rebuild.\n//\n// A restart is not instant: the server drains in-flight work and checkpoints\n// its stores on the way out (typically tens of ms, 5s worst case), and this\n// command waits for that before starting the replacement.\n//\n// The one thing NOT hot-reloaded is the qpq configs themselves: each service's\n// infrastructure.ts is evaluated once at launch and baked into the\n// `quidproquo-dynamic-loader` virtual module — config changes need a full\n// `go:dev:api` restart.\nimport { getAppServiceQpqConfigs, getDevServerRspackConfig } from 'quidproquo-deploy-rspack';\n\nimport { ChildProcess, spawn } from 'child_process';\nimport fs from 'fs';\nimport path from 'path';\nimport { rspack } from '@rspack/core';\n\nimport { hasArgFlag } from '../lib/args';\nimport { primeDeployEnvFromConfig } from '../lib/deployEnv';\nimport { writeDevServerEntry } from '../lib/devServerEntry';\nimport { getRoot } from '../lib/discovery';\nimport { KEEP_OTHER_DEV_SERVERS_FLAG } from '../lib/keepOtherDevServersFlag';\nimport { killChildWithEscalation } from '../lib/killChildWithEscalation';\nimport { killOtherQpqDevProcesses, killStaleListeners, reportPortHolders } from '../lib/killStaleListeners';\nimport { resolveAppSelection } from '../lib/resolveAppSelection';\n\n// 8080/8888 are set in the generated entry; 3001 is the quidproquo-dev-server\n// file-storage (secure URL) default port.\nconst DEV_SERVER_PORTS = [8080, 8888, 3001];\nconst DEV_SERVER_BUNDLE_PATH = path.join('dist', 'qpq', 'dev-server', 'main.js');\n\nexport const goDevApiCommand = async (argv: string[]): Promise<void> => {\n const root = getRoot();\n const appName = await resolveAppSelection({ argv, envVar: 'QPQ_DEV_APP' });\n process.env.QPQ_DEV_APP = appName;\n primeDeployEnvFromConfig(appName);\n console.log(`Dev server for app [${appName}]`);\n\n // The sweep matches on a RELATIVE bundle path, so every checkout's dev\n // server looks like ours and a sibling worktree gets taken down with it.\n // Opting out spares it, at the cost of this one not starting: the ports are\n // the same either way, so what you get instead is EADDRINUSE. Report who\n // holds them, or that failure says nothing about the cause.\n if (hasArgFlag(argv, KEEP_OTHER_DEV_SERVERS_FLAG)) {\n console.log(`${KEEP_OTHER_DEV_SERVERS_FLAG}: leaving other dev servers alone.`);\n reportPortHolders(DEV_SERVER_PORTS);\n } else {\n // Catches a lingering watcher from a previous run even if its spawned\n // child already exited (see killOtherQpqDevProcesses) — then the usual\n // port-based sweep for anything else still bound to our ports.\n killOtherQpqDevProcesses(root);\n killStaleListeners(DEV_SERVER_PORTS, (command) => command.includes(DEV_SERVER_BUNDLE_PATH));\n }\n\n const qpqConfigs = getAppServiceQpqConfigs(root, appName);\n const entry = writeDevServerEntry(root, appName);\n const rspackConfig = getDevServerRspackConfig({ root, entry, qpqConfigs });\n const bundlePath = path.join(root, DEV_SERVER_BUNDLE_PATH);\n const env = { ...process.env, QPQ_DEV_APP: appName };\n\n // The long-lived server process (HTTP 8080 / WS 8888), restarted per rebuild.\n let child: ChildProcess | null = null;\n let restartQueued = false;\n\n const startServer = (): void => {\n child = spawn('node', [bundlePath], { stdio: 'inherit', env });\n child.on('exit', (code) => {\n child = null;\n if (!restartQueued) {\n // Crashed (or exited) on its own — leave it down; the next successful\n // rebuild (i.e. the next file save) brings it back up.\n console.log(`Dev server exited with code ${code}. Save a file to restart it.`);\n }\n });\n };\n\n const restartServer = (): void => {\n if (restartQueued) return;\n if (!child) {\n startServer();\n return;\n }\n restartQueued = true;\n child.once('exit', () => {\n restartQueued = false;\n startServer();\n });\n\n // Not a bare kill: the server drains in-flight work before it exits, so it\n // needs asking properly and then a hard backstop if it wedges. Waiting for\n // the exit is what keeps the ports free for the replacement.\n void killChildWithEscalation(child);\n };\n\n console.log('Bundling dev server (watch mode)...');\n let lastHash: string | null | undefined;\n const compiler = rspack(rspackConfig);\n compiler.watch({ aggregateTimeout: 200 }, (err, stats) => {\n if (err) {\n console.error(err);\n return;\n }\n if (stats?.hasErrors()) {\n console.error(stats.toString({ colors: true, chunks: false, modules: false }));\n console.log('Build failed — dev server not restarted. Fix the error and save again.');\n return;\n }\n // Skip no-op rebuilds (e.g. the virtual-module-triggered second compile at\n // startup): only restart when the output actually changed.\n if (stats?.hash === lastHash && child) {\n return;\n }\n lastHash = stats?.hash;\n console.log(child ? 'Rebuilt. Restarting dev server...' : 'Dev server bundled. Starting...');\n restartServer();\n });\n\n // Wait for the server to finish draining before tearing the watcher down and\n // exiting: closing the compiler first would exit the parent while the child\n // is still writing, which is the race this whole path exists to close.\n //\n // SIGTERM as well as SIGINT, and it is load-bearing rather than symmetry for\n // its own sake. This process is a WRAPPER: the dev server that handles\n // signals and drains its work is the child it spawned. Without a handler\n // here, SIGTERM (what `docker stop` sends, and what any script stopping this\n // programmatically sends) kills the wrapper outright on the default\n // disposition, the child never gets asked to stop, and every guarantee the\n // dev server's shutdown sequence makes is bypassed. go:dev:web already\n // handles both.\n let shuttingDown = false;\n\n const handleShutdownSignal = async (): Promise<void> => {\n if (shuttingDown) {\n return;\n }\n shuttingDown = true;\n\n restartQueued = true; // suppress the crash log / rebuild-restart on our own kill\n\n if (child) {\n await killChildWithEscalation(child);\n }\n\n compiler.close(() => process.exit(0));\n };\n\n process.on('SIGINT', () => {\n void handleShutdownSignal();\n });\n\n process.on('SIGTERM', () => {\n void handleShutdownSignal();\n });\n};\n"]}
@@ -28,11 +28,13 @@ const http_1 = __importDefault(require("http"));
28
28
  const core_1 = require("@rspack/core");
29
29
  const dev_server_1 = require("@rspack/dev-server");
30
30
  const args_1 = require("../lib/args");
31
+ const args_2 = require("../lib/args");
31
32
  const deployEnv_1 = require("../lib/deployEnv");
32
33
  const discovery_1 = require("../lib/discovery");
34
+ const keepOtherDevServersFlag_1 = require("../lib/keepOtherDevServersFlag");
33
35
  const killStaleListeners_1 = require("../lib/killStaleListeners");
34
36
  const resolveAppSelection_1 = require("../lib/resolveAppSelection");
35
- const goDevWebCommand = (argv_1, ...args_2) => __awaiter(void 0, [argv_1, ...args_2], void 0, function* (argv, options = {}) {
37
+ const goDevWebCommand = (argv_1, ...args_3) => __awaiter(void 0, [argv_1, ...args_3], void 0, function* (argv, options = {}) {
36
38
  var _a;
37
39
  const root = (0, discovery_1.getRoot)();
38
40
  const appName = yield (0, resolveAppSelection_1.resolveAppSelection)({ argv, envVar: 'QPQ_DEV_APP' });
@@ -47,7 +49,13 @@ const goDevWebCommand = (argv_1, ...args_2) => __awaiter(void 0, [argv_1, ...arg
47
49
  console.error('No views projects found.');
48
50
  process.exit(1);
49
51
  }
50
- (0, killStaleListeners_1.killStaleListeners)(views.map((v) => v.port), killStaleListeners_1.isQpqCliCommand);
52
+ // See goDevApi for why this is opt-out rather than always-on.
53
+ if ((0, args_2.hasArgFlag)(argv, keepOtherDevServersFlag_1.KEEP_OTHER_DEV_SERVERS_FLAG)) {
54
+ (0, killStaleListeners_1.reportPortHolders)(views.map((v) => v.port));
55
+ }
56
+ else {
57
+ (0, killStaleListeners_1.killStaleListeners)(views.map((v) => v.port), killStaleListeners_1.isQpqCliCommand);
58
+ }
51
59
  // Every RspackDevServer.start() registers its own SIGINT/SIGTERM handler on
52
60
  // process — with a dozen views servers in one process that trips node's
53
61
  // default 10-listener warning. Size the limit to the fleet.
@@ -1 +1 @@
1
- {"version":3,"file":"goDevWeb.js","sourceRoot":"","sources":["../../../src/commands/goDevWeb.ts"],"names":[],"mappings":"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","sourcesContent":["// `qpq go:dev:web` — boots every views microfrontend's Rspack dev server\n// in-process (shell is the MF host app, plus all remotes on their own ports).\n// Everything is served dynamically; remotes resolve at runtime via\n// mf-manifest.json on localhost ports. Programmatic — no per-views\n// rspack.config.ts or npm serve script needed.\n//\n// Usage:\n// qpq go:dev:web\n// qpq go:dev:web --only shell,design\n// qpq go:dev:web --app <name>\nimport { getAllViews, getViewsRspackConfig } from 'quidproquo-deploy-rspack';\n\nimport http from 'http';\nimport { rspack } from '@rspack/core';\nimport { RspackDevServer } from '@rspack/dev-server';\n\nimport { getArgValue } from '../lib/args';\nimport { primeDeployEnvFromConfig } from '../lib/deployEnv';\nimport { getRoot } from '../lib/discovery';\nimport { isQpqCliCommand, killStaleListeners } from '../lib/killStaleListeners';\nimport { resolveAppSelection } from '../lib/resolveAppSelection';\n\nexport type GoDevWebOptions = {\n // The in-place \"(started)\" chip rewrite assumes this command owns the\n // terminal; when stdout is shared with the API dev server (combined\n // `qpq go:dev`) the cursor math breaks. The plain-line fallback is just a\n // wall of \" auth started\" noise interleaved with the API output, so drop\n // the ready status altogether there.\n hideStartedStatus?: boolean;\n};\n\nexport const goDevWebCommand = async (argv: string[], options: GoDevWebOptions = {}): Promise<void> => {\n const root = getRoot();\n const appName = await resolveAppSelection({ argv, envVar: 'QPQ_DEV_APP' });\n\n process.env.LOCAL_DEV_SERVER = 'true';\n process.env.NODE_ENV = 'development';\n primeDeployEnvFromConfig(appName);\n\n const only = getArgValue(argv, '--only')\n ?.split(',')\n .map((s) => s.trim());\n\n let views = getAllViews(root, appName);\n if (only) views = views.filter((v) => only.includes(v.service));\n if (views.length === 0) {\n console.error('No views projects found.');\n process.exit(1);\n }\n\n killStaleListeners(\n views.map((v) => v.port),\n isQpqCliCommand,\n );\n\n // Every RspackDevServer.start() registers its own SIGINT/SIGTERM handler on\n // process — with a dozen views servers in one process that trips node's\n // default 10-listener warning. Size the limit to the fleet.\n process.setMaxListeners(Math.max(process.getMaxListeners(), views.length * 2 + 10));\n\n // Service names are padded to a common width so the \"(started)\" chips align.\n const serviceColumnWidth = Math.max(...views.map((v) => v.service.length));\n\n console.log(`Starting ${views.length} views dev servers for [${appName}]:`);\n for (const v of views) console.log(` http://localhost:${v.port} ${v.service.padEnd(serviceColumnWidth)}`);\n\n const servers: RspackDevServer[] = [];\n let shuttingDown = false;\n\n const shutdown = (code: number): void => {\n if (shuttingDown) return;\n shuttingDown = true;\n Promise.allSettled(servers.map((server) => server.stop())).then(() => process.exit(code));\n };\n\n // The URL list above gets a yellow \"(started)\" chip appended in place as\n // each dev server begins answering HTTP. The in-place rewrite moves the\n // cursor up past everything printed since the list, so every line printed\n // below it MUST go through logBelowList to keep the offset accurate.\n let linesBelowList = 0;\n\n const logBelowList = (line: string): void => {\n console.log(line);\n linesBelowList += 1;\n };\n\n const markStarted = (index: number): void => {\n const v = views[index];\n if (!process.stdout.isTTY) {\n logBelowList(` ${v.service} started`);\n return;\n }\n const line = ` http://localhost:${v.port} ${v.service.padEnd(serviceColumnWidth)} \\x1b[33m(started)\\x1b[0m`;\n const up = linesBelowList + (views.length - index);\n process.stdout.write(`\\x1b[${up}F\\x1b[2K${line}\\x1b[${up}E`);\n };\n\n const pollUntilStarted = (index: number): void => {\n const attempt = (): void => {\n if (shuttingDown) return;\n const req = http.get(\n {\n host: 'localhost',\n port: views[index].port,\n path: '/',\n timeout: 1000,\n },\n (res) => {\n res.resume();\n markStarted(index);\n },\n );\n req.on('error', () => setTimeout(attempt, 500));\n req.on('timeout', () => req.destroy());\n };\n attempt();\n };\n\n for (const [index, view] of views.entries()) {\n const config = getViewsRspackConfig(view.viewsDir);\n const compiler = rspack(config);\n const server = new RspackDevServer(config.devServer || { port: view.port }, compiler);\n servers.push(server);\n\n try {\n await server.start();\n } catch (e) {\n logBelowList(` [${view.service}] failed to start: ${(e as Error).message} — shutting down all views servers.`);\n shutdown(1);\n return;\n }\n\n // markStarted is the only consumer of the ready signal, so there is\n // nothing to poll for when the status is hidden.\n if (!options.hideStartedStatus) pollUntilStarted(index);\n }\n\n process.on('SIGINT', () => shutdown(0));\n process.on('SIGTERM', () => shutdown(0));\n};\n"]}
1
+ {"version":3,"file":"goDevWeb.js","sourceRoot":"","sources":["../../../src/commands/goDevWeb.ts"],"names":[],"mappings":"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","sourcesContent":["// `qpq go:dev:web` — boots every views microfrontend's Rspack dev server\n// in-process (shell is the MF host app, plus all remotes on their own ports).\n// Everything is served dynamically; remotes resolve at runtime via\n// mf-manifest.json on localhost ports. Programmatic — no per-views\n// rspack.config.ts or npm serve script needed.\n//\n// Usage:\n// qpq go:dev:web\n// qpq go:dev:web --only shell,design\n// qpq go:dev:web --app <name>\nimport { getAllViews, getViewsRspackConfig } from 'quidproquo-deploy-rspack';\n\nimport http from 'http';\nimport { rspack } from '@rspack/core';\nimport { RspackDevServer } from '@rspack/dev-server';\n\nimport { getArgValue } from '../lib/args';\nimport { hasArgFlag } from '../lib/args';\nimport { primeDeployEnvFromConfig } from '../lib/deployEnv';\nimport { getRoot } from '../lib/discovery';\nimport { KEEP_OTHER_DEV_SERVERS_FLAG } from '../lib/keepOtherDevServersFlag';\nimport { isQpqCliCommand, killStaleListeners, reportPortHolders } from '../lib/killStaleListeners';\nimport { resolveAppSelection } from '../lib/resolveAppSelection';\n\nexport type GoDevWebOptions = {\n // The in-place \"(started)\" chip rewrite assumes this command owns the\n // terminal; when stdout is shared with the API dev server (combined\n // `qpq go:dev`) the cursor math breaks. The plain-line fallback is just a\n // wall of \" auth started\" noise interleaved with the API output, so drop\n // the ready status altogether there.\n hideStartedStatus?: boolean;\n};\n\nexport const goDevWebCommand = async (argv: string[], options: GoDevWebOptions = {}): Promise<void> => {\n const root = getRoot();\n const appName = await resolveAppSelection({ argv, envVar: 'QPQ_DEV_APP' });\n\n process.env.LOCAL_DEV_SERVER = 'true';\n process.env.NODE_ENV = 'development';\n primeDeployEnvFromConfig(appName);\n\n const only = getArgValue(argv, '--only')\n ?.split(',')\n .map((s) => s.trim());\n\n let views = getAllViews(root, appName);\n if (only) views = views.filter((v) => only.includes(v.service));\n if (views.length === 0) {\n console.error('No views projects found.');\n process.exit(1);\n }\n\n // See goDevApi for why this is opt-out rather than always-on.\n if (hasArgFlag(argv, KEEP_OTHER_DEV_SERVERS_FLAG)) {\n reportPortHolders(views.map((v) => v.port));\n } else {\n killStaleListeners(\n views.map((v) => v.port),\n isQpqCliCommand,\n );\n }\n\n // Every RspackDevServer.start() registers its own SIGINT/SIGTERM handler on\n // process — with a dozen views servers in one process that trips node's\n // default 10-listener warning. Size the limit to the fleet.\n process.setMaxListeners(Math.max(process.getMaxListeners(), views.length * 2 + 10));\n\n // Service names are padded to a common width so the \"(started)\" chips align.\n const serviceColumnWidth = Math.max(...views.map((v) => v.service.length));\n\n console.log(`Starting ${views.length} views dev servers for [${appName}]:`);\n for (const v of views) console.log(` http://localhost:${v.port} ${v.service.padEnd(serviceColumnWidth)}`);\n\n const servers: RspackDevServer[] = [];\n let shuttingDown = false;\n\n const shutdown = (code: number): void => {\n if (shuttingDown) return;\n shuttingDown = true;\n Promise.allSettled(servers.map((server) => server.stop())).then(() => process.exit(code));\n };\n\n // The URL list above gets a yellow \"(started)\" chip appended in place as\n // each dev server begins answering HTTP. The in-place rewrite moves the\n // cursor up past everything printed since the list, so every line printed\n // below it MUST go through logBelowList to keep the offset accurate.\n let linesBelowList = 0;\n\n const logBelowList = (line: string): void => {\n console.log(line);\n linesBelowList += 1;\n };\n\n const markStarted = (index: number): void => {\n const v = views[index];\n if (!process.stdout.isTTY) {\n logBelowList(` ${v.service} started`);\n return;\n }\n const line = ` http://localhost:${v.port} ${v.service.padEnd(serviceColumnWidth)} \\x1b[33m(started)\\x1b[0m`;\n const up = linesBelowList + (views.length - index);\n process.stdout.write(`\\x1b[${up}F\\x1b[2K${line}\\x1b[${up}E`);\n };\n\n const pollUntilStarted = (index: number): void => {\n const attempt = (): void => {\n if (shuttingDown) return;\n const req = http.get(\n {\n host: 'localhost',\n port: views[index].port,\n path: '/',\n timeout: 1000,\n },\n (res) => {\n res.resume();\n markStarted(index);\n },\n );\n req.on('error', () => setTimeout(attempt, 500));\n req.on('timeout', () => req.destroy());\n };\n attempt();\n };\n\n for (const [index, view] of views.entries()) {\n const config = getViewsRspackConfig(view.viewsDir);\n const compiler = rspack(config);\n const server = new RspackDevServer(config.devServer || { port: view.port }, compiler);\n servers.push(server);\n\n try {\n await server.start();\n } catch (e) {\n logBelowList(` [${view.service}] failed to start: ${(e as Error).message} — shutting down all views servers.`);\n shutdown(1);\n return;\n }\n\n // markStarted is the only consumer of the ready signal, so there is\n // nothing to poll for when the status is hidden.\n if (!options.hideStartedStatus) pollUntilStarted(index);\n }\n\n process.on('SIGINT', () => shutdown(0));\n process.on('SIGTERM', () => shutdown(0));\n};\n"]}
@@ -0,0 +1 @@
1
+ export declare const setupCommand: (argv: string[]) => Promise<void>;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.setupCommand = void 0;
13
+ // `qpq setup` — takes a cloud environment from empty to deployed, in order, with
14
+ // every step checked first so the menu shows what is already in place and a
15
+ // rerun on a half-set-up account is safe. The platform (from the environment's
16
+ // entry in deploy.config.json) contributes the steps.
17
+ // --check print each step's status and exit, run nothing
18
+ // --yes run every step without the menu
19
+ // --only a,b run only the named step ids (no menu)
20
+ const args_1 = require("../lib/args");
21
+ const deployEnv_1 = require("../lib/deployEnv");
22
+ const prompts_1 = require("../lib/prompts");
23
+ const resolveAppSelection_1 = require("../lib/resolveAppSelection");
24
+ const platforms_1 = require("../platforms");
25
+ const statusLabel = (result) => (result === null ? '?' : result.done ? 'done' : 'todo');
26
+ const printChecklist = (checked) => {
27
+ for (const { step, result } of checked) {
28
+ const detail = (result === null || result === void 0 ? void 0 : result.detail) ? ` ${result.detail}` : '';
29
+ console.log(` [${statusLabel(result).padEnd(4)}] ${step.name}${detail}`);
30
+ }
31
+ };
32
+ const checkAll = (steps) => __awaiter(void 0, void 0, void 0, function* () {
33
+ const checked = [];
34
+ for (const step of steps) {
35
+ try {
36
+ checked.push({ step, result: yield step.check() });
37
+ }
38
+ catch (error) {
39
+ checked.push({ step, result: { done: false, detail: `check failed: ${error instanceof Error ? error.message : String(error)}` } });
40
+ }
41
+ }
42
+ return checked;
43
+ });
44
+ // Steps not yet done are preselected; done ones can still be picked to rerun.
45
+ const chooseSteps = (checked) => __awaiter(void 0, void 0, void 0, function* () {
46
+ const chosen = yield (0, prompts_1.promptCheckbox)('Setup steps to run (space toggles, enter runs in order)', checked.map(({ step, result }) => ({
47
+ name: `[${statusLabel(result).padEnd(4)}] ${step.name}`,
48
+ value: step,
49
+ checked: !(result === null || result === void 0 ? void 0 : result.done),
50
+ description: result === null || result === void 0 ? void 0 : result.detail,
51
+ })));
52
+ // Always run in checklist order, whatever order they were ticked in.
53
+ return checked.map(({ step }) => step).filter((step) => chosen.includes(step));
54
+ });
55
+ const selectFromArgs = (steps, argv) => {
56
+ if (argv.includes('--yes')) {
57
+ return steps;
58
+ }
59
+ const only = (0, args_1.getArgValue)(argv, '--only');
60
+ if (!only) {
61
+ return null;
62
+ }
63
+ const ids = only.split(',').map((id) => id.trim());
64
+ const unknown = ids.filter((id) => !steps.some((step) => step.id === id));
65
+ if (unknown.length > 0) {
66
+ console.error(`Unknown setup step id(s): ${unknown.join(', ')}. Known: ${steps.map((step) => step.id).join(', ')}`);
67
+ process.exit(1);
68
+ }
69
+ return steps.filter((step) => ids.includes(step.id));
70
+ };
71
+ const setupCommand = (argv) => __awaiter(void 0, void 0, void 0, function* () {
72
+ var _a;
73
+ const appName = yield (0, resolveAppSelection_1.resolveAppSelection)({ argv, envVar: 'DEPLOY_APP_NAME' });
74
+ const { environment, platform } = yield (0, deployEnv_1.resolveDeployEnvironment)(argv, appName);
75
+ const driver = (0, platforms_1.getPlatformDriver)(platform);
76
+ if (!driver.setupSteps) {
77
+ console.log(`The '${platform}' platform has nothing to set up for environment '${environment}'.`);
78
+ return;
79
+ }
80
+ const steps = yield driver.setupSteps(appName, environment);
81
+ console.log(`\nSetup for app '${appName}', environment '${environment}' (${platform})\n`);
82
+ const checked = yield checkAll(steps);
83
+ printChecklist(checked);
84
+ console.log('');
85
+ if (argv.includes('--check')) {
86
+ return;
87
+ }
88
+ const toRun = (_a = selectFromArgs(steps, argv)) !== null && _a !== void 0 ? _a : (yield chooseSteps(checked));
89
+ if (toRun.length === 0) {
90
+ console.log('Nothing selected.');
91
+ return;
92
+ }
93
+ for (const step of toRun) {
94
+ console.log(`\n== ${step.name}\n`);
95
+ yield step.run();
96
+ }
97
+ console.log('\nSetup complete. Final state:\n');
98
+ printChecklist(yield checkAll(steps));
99
+ console.log(`\nNext: deploy the services with npx qpq go:docker all all --app ${appName} --env ${environment}`);
100
+ });
101
+ exports.setupCommand = setupCommand;
102
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/commands/setup.ts"],"names":[],"mappings":"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","sourcesContent":["// `qpq setup` — takes a cloud environment from empty to deployed, in order, with\n// every step checked first so the menu shows what is already in place and a\n// rerun on a half-set-up account is safe. The platform (from the environment's\n// entry in deploy.config.json) contributes the steps.\n// --check print each step's status and exit, run nothing\n// --yes run every step without the menu\n// --only a,b run only the named step ids (no menu)\nimport { getArgValue } from '../lib/args';\nimport { resolveDeployEnvironment } from '../lib/deployEnv';\nimport { promptCheckbox } from '../lib/prompts';\nimport { resolveAppSelection } from '../lib/resolveAppSelection';\nimport { SetupCheckResult, SetupStep } from '../lib/setupStep';\nimport { getPlatformDriver } from '../platforms';\n\ntype CheckedStep = { step: SetupStep; result: SetupCheckResult | null };\n\nconst statusLabel = (result: SetupCheckResult | null): string => (result === null ? '?' : result.done ? 'done' : 'todo');\n\nconst printChecklist = (checked: CheckedStep[]): void => {\n for (const { step, result } of checked) {\n const detail = result?.detail ? ` ${result.detail}` : '';\n console.log(` [${statusLabel(result).padEnd(4)}] ${step.name}${detail}`);\n }\n};\n\nconst checkAll = async (steps: SetupStep[]): Promise<CheckedStep[]> => {\n const checked: CheckedStep[] = [];\n for (const step of steps) {\n try {\n checked.push({ step, result: await step.check() });\n } catch (error) {\n checked.push({ step, result: { done: false, detail: `check failed: ${error instanceof Error ? error.message : String(error)}` } });\n }\n }\n return checked;\n};\n\n// Steps not yet done are preselected; done ones can still be picked to rerun.\nconst chooseSteps = async (checked: CheckedStep[]): Promise<SetupStep[]> => {\n const chosen = await promptCheckbox(\n 'Setup steps to run (space toggles, enter runs in order)',\n checked.map(({ step, result }) => ({\n name: `[${statusLabel(result).padEnd(4)}] ${step.name}`,\n value: step,\n checked: !result?.done,\n description: result?.detail,\n })),\n );\n\n // Always run in checklist order, whatever order they were ticked in.\n return checked.map(({ step }) => step).filter((step) => chosen.includes(step));\n};\n\nconst selectFromArgs = (steps: SetupStep[], argv: string[]): SetupStep[] | null => {\n if (argv.includes('--yes')) {\n return steps;\n }\n\n const only = getArgValue(argv, '--only');\n if (!only) {\n return null;\n }\n\n const ids = only.split(',').map((id) => id.trim());\n const unknown = ids.filter((id) => !steps.some((step) => step.id === id));\n if (unknown.length > 0) {\n console.error(`Unknown setup step id(s): ${unknown.join(', ')}. Known: ${steps.map((step) => step.id).join(', ')}`);\n process.exit(1);\n }\n\n return steps.filter((step) => ids.includes(step.id));\n};\n\nexport const setupCommand = async (argv: string[]): Promise<void> => {\n const appName = await resolveAppSelection({ argv, envVar: 'DEPLOY_APP_NAME' });\n const { environment, platform } = await resolveDeployEnvironment(argv, appName);\n const driver = getPlatformDriver(platform);\n\n if (!driver.setupSteps) {\n console.log(`The '${platform}' platform has nothing to set up for environment '${environment}'.`);\n return;\n }\n\n const steps = await driver.setupSteps(appName, environment);\n\n console.log(`\\nSetup for app '${appName}', environment '${environment}' (${platform})\\n`);\n const checked = await checkAll(steps);\n printChecklist(checked);\n console.log('');\n\n if (argv.includes('--check')) {\n return;\n }\n\n const toRun = selectFromArgs(steps, argv) ?? (await chooseSteps(checked));\n if (toRun.length === 0) {\n console.log('Nothing selected.');\n return;\n }\n\n for (const step of toRun) {\n console.log(`\\n== ${step.name}\\n`);\n await step.run();\n }\n\n console.log('\\nSetup complete. Final state:\\n');\n printChecklist(await checkAll(steps));\n console.log(`\\nNext: deploy the services with npx qpq go:docker all all --app ${appName} --env ${environment}`);\n};\n"]}
@@ -1,2 +1,3 @@
1
1
  export declare const getArgValue: (argv: string[], flag: string) => string | undefined;
2
+ export declare const hasArgFlag: (argv: string[], flag: string) => boolean;
2
3
  export declare const getPositionalArgs: (argv: string[], valueFlags: string[]) => string[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getPositionalArgs = exports.getArgValue = void 0;
3
+ exports.getPositionalArgs = exports.hasArgFlag = exports.getArgValue = void 0;
4
4
  // Flag parsing over the command's argv (everything after the command name).
5
5
  const getArgValue = (argv, flag) => {
6
6
  const eq = argv.find((a) => a.startsWith(`${flag}=`));
@@ -10,6 +10,16 @@ const getArgValue = (argv, flag) => {
10
10
  return idx >= 0 ? argv[idx + 1] : undefined;
11
11
  };
12
12
  exports.getArgValue = getArgValue;
13
+ // A presence-only flag: `--foo`. `--foo=false` is honoured as off, so a
14
+ // wrapper script can pass the flag through with a computed value rather than
15
+ // having to conditionally omit it.
16
+ const hasArgFlag = (argv, flag) => {
17
+ const eq = argv.find((a) => a.startsWith(`${flag}=`));
18
+ if (eq)
19
+ return eq.slice(`${flag}=`.length) !== 'false';
20
+ return argv.includes(flag);
21
+ };
22
+ exports.hasArgFlag = hasArgFlag;
13
23
  // Positional args = non-flag args, skipping values consumed by known flags.
14
24
  const getPositionalArgs = (argv, valueFlags) => argv.filter((arg, index) => {
15
25
  if (arg.startsWith('--'))
@@ -1 +1 @@
1
- {"version":3,"file":"args.js","sourceRoot":"","sources":["../../../src/lib/args.ts"],"names":[],"mappings":";;;AAAA,4EAA4E;AACrE,MAAM,WAAW,GAAG,CAAC,IAAc,EAAE,IAAY,EAAsB,EAAE;IAC9E,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IACtD,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9C,CAAC,CAAC;AALW,QAAA,WAAW,eAKtB;AAEF,4EAA4E;AACrE,MAAM,iBAAiB,GAAG,CAAC,IAAc,EAAE,UAAoB,EAAY,EAAE,CAClF,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;IACzB,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC,QAAQ,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AALQ,QAAA,iBAAiB,qBAKzB","sourcesContent":["// Flag parsing over the command's argv (everything after the command name).\nexport const getArgValue = (argv: string[], flag: string): string | undefined => {\n const eq = argv.find((a) => a.startsWith(`${flag}=`));\n if (eq) return eq.slice(`${flag}=`.length);\n const idx = argv.indexOf(flag);\n return idx >= 0 ? argv[idx + 1] : undefined;\n};\n\n// Positional args = non-flag args, skipping values consumed by known flags.\nexport const getPositionalArgs = (argv: string[], valueFlags: string[]): string[] =>\n argv.filter((arg, index) => {\n if (arg.startsWith('--')) return false;\n const previous = argv[index - 1];\n return !(previous && valueFlags.includes(previous));\n });\n"]}
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../../src/lib/args.ts"],"names":[],"mappings":";;;AAAA,4EAA4E;AACrE,MAAM,WAAW,GAAG,CAAC,IAAc,EAAE,IAAY,EAAsB,EAAE;IAC9E,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IACtD,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9C,CAAC,CAAC;AALW,QAAA,WAAW,eAKtB;AAEF,wEAAwE;AACxE,6EAA6E;AAC7E,mCAAmC;AAC5B,MAAM,UAAU,GAAG,CAAC,IAAc,EAAE,IAAY,EAAW,EAAE;IAClE,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IACtD,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC;IAEvD,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC;AALW,QAAA,UAAU,cAKrB;AAEF,4EAA4E;AACrE,MAAM,iBAAiB,GAAG,CAAC,IAAc,EAAE,UAAoB,EAAY,EAAE,CAClF,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;IACzB,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACjC,OAAO,CAAC,CAAC,QAAQ,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AALQ,QAAA,iBAAiB,qBAKzB","sourcesContent":["// Flag parsing over the command's argv (everything after the command name).\nexport const getArgValue = (argv: string[], flag: string): string | undefined => {\n const eq = argv.find((a) => a.startsWith(`${flag}=`));\n if (eq) return eq.slice(`${flag}=`.length);\n const idx = argv.indexOf(flag);\n return idx >= 0 ? argv[idx + 1] : undefined;\n};\n\n// A presence-only flag: `--foo`. `--foo=false` is honoured as off, so a\n// wrapper script can pass the flag through with a computed value rather than\n// having to conditionally omit it.\nexport const hasArgFlag = (argv: string[], flag: string): boolean => {\n const eq = argv.find((a) => a.startsWith(`${flag}=`));\n if (eq) return eq.slice(`${flag}=`.length) !== 'false';\n\n return argv.includes(flag);\n};\n\n// Positional args = non-flag args, skipping values consumed by known flags.\nexport const getPositionalArgs = (argv: string[], valueFlags: string[]): string[] =>\n argv.filter((arg, index) => {\n if (arg.startsWith('--')) return false;\n const previous = argv[index - 1];\n return !(previous && valueFlags.includes(previous));\n });\n"]}
@@ -0,0 +1 @@
1
+ export declare const KEEP_OTHER_DEV_SERVERS_FLAG = "--keep-other-dev-servers";
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KEEP_OTHER_DEV_SERVERS_FLAG = void 0;
4
+ // Opt out of the pre-start sweep that kills anything already holding the dev
5
+ // ports, including a dev server belonging to a different checkout of the same
6
+ // repo. Off by default, so the existing behaviour is unchanged.
7
+ //
8
+ // One flag rather than one per command: `go:dev` hands the same argv to both
9
+ // `go:dev:api` and `go:dev:web`, so declaring it once covers all three.
10
+ exports.KEEP_OTHER_DEV_SERVERS_FLAG = '--keep-other-dev-servers';
11
+ //# sourceMappingURL=keepOtherDevServersFlag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keepOtherDevServersFlag.js","sourceRoot":"","sources":["../../../src/lib/keepOtherDevServersFlag.ts"],"names":[],"mappings":";;;AAAA,6EAA6E;AAC7E,8EAA8E;AAC9E,gEAAgE;AAChE,EAAE;AACF,6EAA6E;AAC7E,wEAAwE;AAC3D,QAAA,2BAA2B,GAAG,0BAA0B,CAAC","sourcesContent":["// Opt out of the pre-start sweep that kills anything already holding the dev\n// ports, including a dev server belonging to a different checkout of the same\n// repo. Off by default, so the existing behaviour is unchanged.\n//\n// One flag rather than one per command: `go:dev` hands the same argv to both\n// `go:dev:api` and `go:dev:web`, so declaring it once covers all three.\nexport const KEEP_OTHER_DEV_SERVERS_FLAG = '--keep-other-dev-servers';\n"]}
@@ -1,3 +1,17 @@
1
1
  export declare const isQpqCliCommand: (command: string) => boolean;
2
2
  export declare const killOtherQpqDevProcesses: (root: string) => void;
3
+ /**
4
+ * Say what is holding these ports, without touching it.
5
+ *
6
+ * The other half of --keep-other-dev-servers: sparing a sibling checkout's dev
7
+ * server means this one cannot bind, and an unexplained EADDRINUSE is a worse
8
+ * experience than the silent kill it replaces. Naming the pid and the command
9
+ * turns "it just died" into something you can act on.
10
+ *
11
+ * Worded as a prediction, not a fact, because it IS one: this runs before the
12
+ * bundle step, so by the time anything binds - seconds later - the holder may
13
+ * well have gone. Stating "port in use" and then starting cleanly reads like
14
+ * the warning was wrong.
15
+ */
16
+ export declare const reportPortHolders: (ports: number[]) => void;
3
17
  export declare const killStaleListeners: (ports: number[], isOurs: (command: string) => boolean) => void;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.killStaleListeners = exports.killOtherQpqDevProcesses = exports.isQpqCliCommand = void 0;
6
+ exports.killStaleListeners = exports.reportPortHolders = exports.killOtherQpqDevProcesses = exports.isQpqCliCommand = void 0;
7
7
  // Pre-start sweep shared by the dev-server commands: a previous run whose
8
8
  // wrapper died without killing its listener (closed terminal, SIGKILL) leaves
9
9
  // an orphaned process holding a dev port, and the next launch dies with
@@ -153,29 +153,56 @@ const killOtherQpqDevProcesses = (root) => {
153
153
  }
154
154
  };
155
155
  exports.killOtherQpqDevProcesses = killOtherQpqDevProcesses;
156
- const killStaleListeners = (ports, isOurs) => {
157
- for (const port of ports) {
158
- let pids = [];
156
+ // Whatever is currently listening on a port, as pid + command line. Empty when
157
+ // the port is free, or when lsof is unavailable.
158
+ const getPortListeners = (port) => {
159
+ let pids = [];
160
+ try {
161
+ pids = (0, child_process_1.execSync)(`lsof -t -iTCP:${port} -sTCP:LISTEN`, {
162
+ stdio: ['ignore', 'pipe', 'ignore'],
163
+ })
164
+ .toString()
165
+ .split('\n')
166
+ .map((line) => Number(line.trim()))
167
+ .filter(Boolean);
168
+ }
169
+ catch (_a) {
170
+ return []; // lsof exits non-zero when nothing is listening
171
+ }
172
+ return pids.flatMap((pid) => {
159
173
  try {
160
- pids = (0, child_process_1.execSync)(`lsof -t -iTCP:${port} -sTCP:LISTEN`, {
161
- stdio: ['ignore', 'pipe', 'ignore'],
162
- })
163
- .toString()
164
- .split('\n')
165
- .map((line) => Number(line.trim()))
166
- .filter(Boolean);
174
+ return [{ pid, command: (0, child_process_1.execSync)(`ps -p ${pid} -o command=`).toString().trim() }];
167
175
  }
168
176
  catch (_a) {
169
- continue; // lsof exits non-zero when nothing is listening
177
+ return []; // already gone
170
178
  }
171
- for (const pid of pids) {
172
- let command = '';
173
- try {
174
- command = (0, child_process_1.execSync)(`ps -p ${pid} -o command=`).toString().trim();
175
- }
176
- catch (_b) {
177
- continue; // already gone
178
- }
179
+ });
180
+ };
181
+ /**
182
+ * Say what is holding these ports, without touching it.
183
+ *
184
+ * The other half of --keep-other-dev-servers: sparing a sibling checkout's dev
185
+ * server means this one cannot bind, and an unexplained EADDRINUSE is a worse
186
+ * experience than the silent kill it replaces. Naming the pid and the command
187
+ * turns "it just died" into something you can act on.
188
+ *
189
+ * Worded as a prediction, not a fact, because it IS one: this runs before the
190
+ * bundle step, so by the time anything binds - seconds later - the holder may
191
+ * well have gone. Stating "port in use" and then starting cleanly reads like
192
+ * the warning was wrong.
193
+ */
194
+ const reportPortHolders = (ports) => {
195
+ for (const port of ports) {
196
+ for (const { pid, command } of getPortListeners(port)) {
197
+ console.warn(`Port ${port} is currently held by pid ${pid}: ${command}`);
198
+ console.warn(` Starting anyway. If it still holds port ${port} once this server binds, startup will fail with EADDRINUSE.`);
199
+ }
200
+ }
201
+ };
202
+ exports.reportPortHolders = reportPortHolders;
203
+ const killStaleListeners = (ports, isOurs) => {
204
+ for (const port of ports) {
205
+ for (const { pid, command } of getPortListeners(port)) {
179
206
  if (isOurs(command)) {
180
207
  console.log(`Killing stale dev server on port ${port} (pid ${pid})`);
181
208
  process.kill(pid);