quidproquo-cli 0.1.6 → 0.1.8

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 (138) hide show
  1. package/docker/dev-server/Dockerfile +7 -9
  2. package/lib/commonjs/cli/commandMenu.d.ts +2 -0
  3. package/lib/commonjs/cli/commandMenu.js +81 -0
  4. package/lib/commonjs/cli/commandMenu.js.map +1 -0
  5. package/lib/commonjs/cli/commandRegistry.d.ts +10 -0
  6. package/lib/commonjs/cli/commandRegistry.js +131 -0
  7. package/lib/commonjs/cli/commandRegistry.js.map +1 -0
  8. package/lib/commonjs/cli/runCli.js +49 -49
  9. package/lib/commonjs/cli/runCli.js.map +1 -1
  10. package/lib/commonjs/commands/clearResources.d.ts +1 -0
  11. package/lib/commonjs/commands/clearResources.js +36 -0
  12. package/lib/commonjs/commands/clearResources.js.map +1 -0
  13. package/lib/commonjs/commands/go.js +3 -1
  14. package/lib/commonjs/commands/go.js.map +1 -1
  15. package/lib/commonjs/commands/goDev.js +23 -122
  16. package/lib/commonjs/commands/goDev.js.map +1 -1
  17. package/lib/commonjs/commands/goDevApi.d.ts +1 -0
  18. package/lib/commonjs/commands/goDevApi.js +110 -0
  19. package/lib/commonjs/commands/goDevApi.js.map +1 -0
  20. package/lib/commonjs/commands/goDevWeb.d.ts +4 -1
  21. package/lib/commonjs/commands/goDevWeb.js +4 -2
  22. package/lib/commonjs/commands/goDevWeb.js.map +1 -1
  23. package/lib/commonjs/commands/goDocker.js +3 -1
  24. package/lib/commonjs/commands/goDocker.js.map +1 -1
  25. package/lib/commonjs/commands/hooks.js +23 -5
  26. package/lib/commonjs/commands/hooks.js.map +1 -1
  27. package/lib/commonjs/lib/clearResourcesPlan.d.ts +15 -0
  28. package/lib/commonjs/lib/clearResourcesPlan.js +80 -0
  29. package/lib/commonjs/lib/clearResourcesPlan.js.map +1 -0
  30. package/lib/commonjs/lib/deployPrompts.d.ts +1 -0
  31. package/lib/commonjs/lib/deployPrompts.js +40 -2
  32. package/lib/commonjs/lib/deployPrompts.js.map +1 -1
  33. package/lib/commonjs/lib/devServerEntry.js +3 -0
  34. package/lib/commonjs/lib/devServerEntry.js.map +1 -1
  35. package/lib/commonjs/lib/hooks.d.ts +2 -1
  36. package/lib/commonjs/lib/hooks.js +100 -5
  37. package/lib/commonjs/lib/hooks.js.map +1 -1
  38. package/lib/commonjs/lib/killStaleListeners.d.ts +3 -0
  39. package/lib/commonjs/lib/killStaleListeners.js +170 -0
  40. package/lib/commonjs/lib/killStaleListeners.js.map +1 -0
  41. package/lib/commonjs/lib/prompts.d.ts +10 -0
  42. package/lib/commonjs/lib/prompts.js +15 -1
  43. package/lib/commonjs/lib/prompts.js.map +1 -1
  44. package/lib/commonjs/lib/qpqConfigs.d.ts +1 -0
  45. package/lib/commonjs/lib/qpqConfigs.js +9 -1
  46. package/lib/commonjs/lib/qpqConfigs.js.map +1 -1
  47. package/lib/commonjs/lib/resolveAppSelection.js +1 -1
  48. package/lib/commonjs/lib/resolveAppSelection.js.map +1 -1
  49. package/lib/commonjs/lib/runTasks.d.ts +5 -0
  50. package/lib/commonjs/lib/runTasks.js +82 -1
  51. package/lib/commonjs/lib/runTasks.js.map +1 -1
  52. package/lib/commonjs/lib/typeWatcher.d.ts +2 -0
  53. package/lib/commonjs/lib/typeWatcher.js +59 -0
  54. package/lib/commonjs/lib/typeWatcher.js.map +1 -0
  55. package/lib/commonjs/platforms/aws/cdkApp.js +5 -1
  56. package/lib/commonjs/platforms/aws/cdkApp.js.map +1 -1
  57. package/lib/commonjs/platforms/aws/clearResources.d.ts +2 -0
  58. package/lib/commonjs/platforms/aws/clearResources.js +167 -0
  59. package/lib/commonjs/platforms/aws/clearResources.js.map +1 -0
  60. package/lib/commonjs/platforms/aws/go.js +15 -1
  61. package/lib/commonjs/platforms/aws/go.js.map +1 -1
  62. package/lib/commonjs/platforms/aws/goDocker.js +76 -8
  63. package/lib/commonjs/platforms/aws/goDocker.js.map +1 -1
  64. package/lib/commonjs/platforms/aws/index.js +2 -0
  65. package/lib/commonjs/platforms/aws/index.js.map +1 -1
  66. package/lib/commonjs/platforms/docker/go.js +2 -2
  67. package/lib/commonjs/platforms/docker/go.js.map +1 -1
  68. package/lib/commonjs/platforms/types.d.ts +2 -0
  69. package/lib/commonjs/platforms/types.js.map +1 -1
  70. package/lib/esm/cli/commandMenu.d.ts +2 -0
  71. package/lib/esm/cli/commandMenu.js +67 -0
  72. package/lib/esm/cli/commandMenu.js.map +1 -0
  73. package/lib/esm/cli/commandRegistry.d.ts +10 -0
  74. package/lib/esm/cli/commandRegistry.js +115 -0
  75. package/lib/esm/cli/commandRegistry.js.map +1 -0
  76. package/lib/esm/cli/runCli.js +49 -49
  77. package/lib/esm/cli/runCli.js.map +1 -1
  78. package/lib/esm/commands/clearResources.d.ts +1 -0
  79. package/lib/esm/commands/clearResources.js +23 -0
  80. package/lib/esm/commands/clearResources.js.map +1 -0
  81. package/lib/esm/commands/go.js +3 -2
  82. package/lib/esm/commands/go.js.map +1 -1
  83. package/lib/esm/commands/goDev.js +24 -120
  84. package/lib/esm/commands/goDev.js.map +1 -1
  85. package/lib/esm/commands/goDevApi.d.ts +1 -0
  86. package/lib/esm/commands/goDevApi.js +94 -0
  87. package/lib/esm/commands/goDevApi.js.map +1 -0
  88. package/lib/esm/commands/goDevWeb.d.ts +4 -1
  89. package/lib/esm/commands/goDevWeb.js +4 -2
  90. package/lib/esm/commands/goDevWeb.js.map +1 -1
  91. package/lib/esm/commands/goDocker.js +3 -2
  92. package/lib/esm/commands/goDocker.js.map +1 -1
  93. package/lib/esm/commands/hooks.js +21 -6
  94. package/lib/esm/commands/hooks.js.map +1 -1
  95. package/lib/esm/lib/clearResourcesPlan.d.ts +15 -0
  96. package/lib/esm/lib/clearResourcesPlan.js +66 -0
  97. package/lib/esm/lib/clearResourcesPlan.js.map +1 -0
  98. package/lib/esm/lib/deployPrompts.d.ts +1 -0
  99. package/lib/esm/lib/deployPrompts.js +38 -1
  100. package/lib/esm/lib/deployPrompts.js.map +1 -1
  101. package/lib/esm/lib/devServerEntry.js +3 -0
  102. package/lib/esm/lib/devServerEntry.js.map +1 -1
  103. package/lib/esm/lib/hooks.d.ts +2 -1
  104. package/lib/esm/lib/hooks.js +97 -5
  105. package/lib/esm/lib/hooks.js.map +1 -1
  106. package/lib/esm/lib/killStaleListeners.d.ts +3 -0
  107. package/lib/esm/lib/killStaleListeners.js +161 -0
  108. package/lib/esm/lib/killStaleListeners.js.map +1 -0
  109. package/lib/esm/lib/prompts.d.ts +10 -0
  110. package/lib/esm/lib/prompts.js +12 -0
  111. package/lib/esm/lib/prompts.js.map +1 -1
  112. package/lib/esm/lib/qpqConfigs.d.ts +1 -0
  113. package/lib/esm/lib/qpqConfigs.js +7 -0
  114. package/lib/esm/lib/qpqConfigs.js.map +1 -1
  115. package/lib/esm/lib/resolveAppSelection.js +1 -1
  116. package/lib/esm/lib/resolveAppSelection.js.map +1 -1
  117. package/lib/esm/lib/runTasks.d.ts +5 -0
  118. package/lib/esm/lib/runTasks.js +80 -0
  119. package/lib/esm/lib/runTasks.js.map +1 -1
  120. package/lib/esm/lib/typeWatcher.d.ts +2 -0
  121. package/lib/esm/lib/typeWatcher.js +51 -0
  122. package/lib/esm/lib/typeWatcher.js.map +1 -0
  123. package/lib/esm/platforms/aws/cdkApp.js +5 -1
  124. package/lib/esm/platforms/aws/cdkApp.js.map +1 -1
  125. package/lib/esm/platforms/aws/clearResources.d.ts +2 -0
  126. package/lib/esm/platforms/aws/clearResources.js +151 -0
  127. package/lib/esm/platforms/aws/clearResources.js.map +1 -0
  128. package/lib/esm/platforms/aws/go.js +16 -2
  129. package/lib/esm/platforms/aws/go.js.map +1 -1
  130. package/lib/esm/platforms/aws/goDocker.js +77 -9
  131. package/lib/esm/platforms/aws/goDocker.js.map +1 -1
  132. package/lib/esm/platforms/aws/index.js +2 -0
  133. package/lib/esm/platforms/aws/index.js.map +1 -1
  134. package/lib/esm/platforms/docker/go.js +2 -2
  135. package/lib/esm/platforms/docker/go.js.map +1 -1
  136. package/lib/esm/platforms/types.d.ts +2 -0
  137. package/lib/esm/platforms/types.js.map +1 -1
  138. package/package.json +12 -10
@@ -0,0 +1,115 @@
1
+ // Single source of truth for every qpq command: its name, the one-line
2
+ // summary (shared by `qpq help` and the interactive menu) and a runner.
3
+ //
4
+ // Command modules are required lazily inside each runner so a simple
5
+ // `qpq help` doesn't pay for the whole toolchain, and so ts-node can be
6
+ // registered before any command module (or anything it requires) loads.
7
+ import { promptText } from '../lib/prompts';
8
+ // `qpq hooks` needs a hook name; when launched from the menu there is no
9
+ // argv to pass it in, so ask.
10
+ const promptHookName = async () => [
11
+ await promptText('Hook name (runs qpq:<name>, or plain <name>, in every app/package that defines it):'),
12
+ ];
13
+ /* eslint-disable @typescript-eslint/no-require-imports */
14
+ export const cliCommands = [
15
+ {
16
+ name: 'go',
17
+ usageArgs: '[svc] [stack]',
18
+ summary: 'Deploy services (interactive; platform from deploy.config.json, default aws).',
19
+ usageExtra: [
20
+ 'Pass positional args to skip prompts, e.g. qpq go all all — svc is',
21
+ "'all' | comma-list | account | domain | bootstrap; stack is all|inf|api|web|views.",
22
+ "A full 'all all' (or 'all api') deploy also publishes federated backends.",
23
+ ],
24
+ run: (a) => require('../commands/go').goCommand(a),
25
+ },
26
+ {
27
+ name: 'go:docker',
28
+ summary: 'Same as go (incl. positional args), but deploys in parallel via docker',
29
+ run: (a) => require('../commands/goDocker').goDockerCommand(a),
30
+ },
31
+ {
32
+ name: 'go:dev',
33
+ summary: 'Run the full local dev stack (api + web) in one process',
34
+ run: (a) => require('../commands/goDev').goDevCommand(a),
35
+ },
36
+ {
37
+ name: 'go:dev:api',
38
+ summary: 'Run the local API dev server (hot reload)',
39
+ run: (a) => require('../commands/goDevApi').goDevApiCommand(a),
40
+ },
41
+ {
42
+ name: 'go:dev:web',
43
+ summary: 'Run every views microfrontend dev server',
44
+ run: (a) => require('../commands/goDevWeb').goDevWebCommand(a),
45
+ },
46
+ {
47
+ name: 'teardown',
48
+ summary: 'Destroy web/api/inf stacks for selected services (interactive)',
49
+ run: (a) => require('../commands/teardown').teardownCommand(a),
50
+ },
51
+ {
52
+ name: 'clear-resources',
53
+ summary: 'Empty selected buckets/tables (data only, stacks untouched; interactive)',
54
+ run: (a) => require('../commands/clearResources').clearResourcesCommand(a),
55
+ },
56
+ {
57
+ name: 'synth',
58
+ usageArgs: '[service]',
59
+ summary: 'Synth QPQ configs to dist/apps/<app>/infrastructure',
60
+ run: (a) => require('../commands/synth').synthCommand(a),
61
+ },
62
+ {
63
+ name: 'prep',
64
+ summary: 'Regenerate apps/<app>/tsconfig.federated.json',
65
+ run: (a) => require('../commands/prep').prepCommand(a),
66
+ },
67
+ {
68
+ name: 'publish',
69
+ summary: 'Build + upload + deploy federated remotes',
70
+ run: (a) => require('../commands/publish').publishCommand(a),
71
+ },
72
+ {
73
+ name: 'publish:build',
74
+ summary: 'Build federated remotes locally',
75
+ run: (a) => require('../commands/publish').publishBuildCommand(a),
76
+ },
77
+ {
78
+ name: 'publish:upload',
79
+ summary: 'Upload built version dirs (nothing goes live)',
80
+ run: (a) => require('../commands/publish').publishUploadCommand(a),
81
+ },
82
+ {
83
+ name: 'publish:deploy',
84
+ summary: 'Flip manifests to the uploaded versions',
85
+ run: (a) => require('../commands/publish').publishDeployCommand(a),
86
+ },
87
+ {
88
+ name: 'hooks',
89
+ usageArgs: '<name>',
90
+ summary: 'Run qpq:<name> (or plain <name>) in every app/package that defines it (dependency ordered, parallel; --jobs=N)',
91
+ promptArgs: promptHookName,
92
+ run: (a) => require('../commands/hooks').hooksCommand(a),
93
+ },
94
+ ];
95
+ /* eslint-enable @typescript-eslint/no-require-imports */
96
+ // Width of the "name [args]" column in the usage text.
97
+ const usageColumnWidth = 17;
98
+ export const buildUsage = () => {
99
+ const commandLines = cliCommands.flatMap((command) => {
100
+ const nameAndArgs = command.usageArgs ? `${command.name} ${command.usageArgs}` : command.name;
101
+ const extraLines = (command.usageExtra ?? []).map((line) => ` ${''.padEnd(usageColumnWidth)}${line}`);
102
+ return [` ${nameAndArgs.padEnd(usageColumnWidth)}${command.summary}`, ...extraLines];
103
+ });
104
+ return [
105
+ 'qpq — build, dev and deploy orchestration for QPQ apps',
106
+ '',
107
+ 'Usage: qpq <command> [--app <name>] [--env <name>] [--platform <name>]',
108
+ ' qpq (no command opens an interactive menu)',
109
+ '',
110
+ 'Commands:',
111
+ ...commandLines,
112
+ '',
113
+ ].join('\n');
114
+ };
115
+ //# sourceMappingURL=commandRegistry.js.map
@@ -0,0 +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: '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: '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,21 +1,5 @@
1
- const usage = `qpq build, dev and deploy orchestration for QPQ apps
2
-
3
- Usage: qpq <command> [--app <name>] [--env <name>] [--platform <name>]
4
-
5
- Commands:
6
- go Deploy services (interactive; platform from deploy.config.json, default aws)
7
- go:docker Same as go, but deploys in parallel via docker
8
- go:dev Run the local dev server (hot reload)
9
- go:dev:web Run every views microfrontend dev server
10
- teardown Destroy web/api/inf stacks for selected services (interactive)
11
- synth [service] Synth QPQ configs to dist/apps/<app>/infrastructure
12
- prep Regenerate apps/<app>/tsconfig.federated.json
13
- publish Build + upload + deploy federated remotes
14
- publish:build Build federated remotes locally
15
- publish:upload Upload built version dirs (nothing goes live)
16
- publish:deploy Flip manifests to the uploaded versions
17
- hooks <name> Run qpq:<name> in every app that defines it
18
- `;
1
+ import { promptCommandFromMenu } from './commandMenu';
2
+ import { buildUsage, cliCommands } from './commandRegistry';
19
3
  // Service infrastructure.ts files (and app config fragments) are TypeScript —
20
4
  // register ts-node's transpile-only require hook so the CLI can require them
21
5
  // directly, using the consumer root's tsconfig.json ts-node settings.
@@ -29,44 +13,60 @@ const registerTsNode = () => {
29
13
  // eslint-disable-next-line @typescript-eslint/no-require-imports
30
14
  require('ts-node').register({ transpileOnly: true });
31
15
  };
16
+ // Inquirer throws ExitPromptError on ctrl-c; treat that as a quiet exit
17
+ // rather than a command failure.
18
+ const isPromptExit = (error) => error instanceof Error && error.name === 'ExitPromptError';
19
+ // Bare `qpq` on a terminal: drill through the command menu, gathering any
20
+ // arguments the picked command needs. Returns null when the user bails out.
21
+ const resolveCommandInteractively = async () => {
22
+ try {
23
+ const command = await promptCommandFromMenu(cliCommands);
24
+ const commandArgv = command.promptArgs ? await command.promptArgs() : [];
25
+ return { command, commandArgv };
26
+ }
27
+ catch (error) {
28
+ if (isPromptExit(error)) {
29
+ return null;
30
+ }
31
+ throw error;
32
+ }
33
+ };
32
34
  export const runCli = async (argv) => {
33
- const [command, ...commandArgv] = argv;
34
- if (!command || command === 'help' || command === '--help') {
35
- console.log(usage);
35
+ const [commandName, ...argvRest] = argv;
36
+ if (commandName === 'help' || commandName === '--help') {
37
+ console.log(buildUsage());
36
38
  return;
37
39
  }
38
- registerTsNode();
39
- // Commands are required lazily so a simple `qpq help` doesn't pay for the
40
- // whole toolchain, and so ts-node is registered before any command module
41
- // (or anything it requires) loads.
42
- /* eslint-disable @typescript-eslint/no-require-imports */
43
- const commands = {
44
- go: (a) => require('../commands/go').goCommand(a),
45
- 'go:docker': (a) => require('../commands/goDocker').goDockerCommand(a),
46
- 'go:dev': (a) => require('../commands/goDev').goDevCommand(a),
47
- 'go:dev:web': (a) => require('../commands/goDevWeb').goDevWebCommand(a),
48
- teardown: (a) => require('../commands/teardown').teardownCommand(a),
49
- synth: (a) => require('../commands/synth').synthCommand(a),
50
- prep: (a) => require('../commands/prep').prepCommand(a),
51
- publish: (a) => require('../commands/publish').publishCommand(a),
52
- 'publish:build': (a) => require('../commands/publish').publishBuildCommand(a),
53
- 'publish:upload': (a) => require('../commands/publish').publishUploadCommand(a),
54
- 'publish:deploy': (a) => require('../commands/publish').publishDeployCommand(a),
55
- hooks: (a) => require('../commands/hooks').hooksCommand(a),
56
- };
57
- /* eslint-enable @typescript-eslint/no-require-imports */
58
- const run = commands[command];
59
- if (!run) {
60
- console.error(`Unknown command: ${command}`);
61
- console.log(usage);
62
- process.exitCode = 1;
63
- return;
40
+ let command;
41
+ let commandArgv = [];
42
+ if (!commandName) {
43
+ // Only prompt on a real terminal; piped/CI invocations get the usage text.
44
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
45
+ console.log(buildUsage());
46
+ return;
47
+ }
48
+ const resolved = await resolveCommandInteractively();
49
+ if (!resolved) {
50
+ return;
51
+ }
52
+ ({ command, commandArgv } = resolved);
64
53
  }
54
+ else {
55
+ command = cliCommands.find((c) => c.name === commandName);
56
+ commandArgv = argvRest;
57
+ if (!command) {
58
+ console.error(`Unknown command: ${commandName}`);
59
+ console.log(buildUsage());
60
+ process.exitCode = 1;
61
+ return;
62
+ }
63
+ }
64
+ registerTsNode();
65
65
  try {
66
- await run(commandArgv);
66
+ await command.run(commandArgv);
67
67
  }
68
68
  catch (error) {
69
- console.error(`qpq ${command} failed:`, error);
69
+ console.error(`qpq ${command.name} failed:`, error);
70
70
  process.exitCode = 1;
71
71
  }
72
72
  };
@@ -1 +1 @@
1
- {"version":3,"file":"runCli.js","sourceRoot":"","sources":["../../../src/cli/runCli.ts"],"names":[],"mappings":"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","sourcesContent":["const usage = `qpq build, dev and deploy orchestration for QPQ apps\n\nUsage: qpq <command> [--app <name>] [--env <name>] [--platform <name>]\n\nCommands:\n go Deploy services (interactive; platform from deploy.config.json, default aws)\n go:docker Same as go, but deploys in parallel via docker\n go:dev Run the local dev server (hot reload)\n go:dev:web Run every views microfrontend dev server\n teardown Destroy web/api/inf stacks for selected services (interactive)\n synth [service] Synth QPQ configs to dist/apps/<app>/infrastructure\n prep Regenerate apps/<app>/tsconfig.federated.json\n publish Build + upload + deploy federated remotes\n publish:build Build federated remotes locally\n publish:upload Upload built version dirs (nothing goes live)\n publish:deploy Flip manifests to the uploaded versions\n hooks <name> Run qpq:<name> in every app that defines it\n`;\n\n// Service infrastructure.ts files (and app config fragments) are TypeScript —\n// register ts-node's transpile-only require hook so the CLI can require them\n// directly, using the consumer root's tsconfig.json ts-node settings.\n//\n// CONTRACT: JavaScript apps (create-qpq-app --language javascript) also lean\n// on this hook — their tsconfig.json ts-node block sets allowJs (so their\n// ESM-syntax infrastructure.js compiles at require time) and scope/scopeDir\n// (so the hook never touches quidproquo libs outside the app). Any\n// replacement for ts-node here must honour those consumer tsconfig options.\nconst registerTsNode = (): void => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require('ts-node').register({ transpileOnly: true });\n};\n\nexport const runCli = async (argv: string[]): Promise<void> => {\n const [command, ...commandArgv] = argv;\n\n if (!command || command === 'help' || command === '--help') {\n console.log(usage);\n return;\n }\n\n registerTsNode();\n\n // Commands are required lazily so a simple `qpq help` doesn't pay for the\n // whole toolchain, and so ts-node is registered before any command module\n // (or anything it requires) loads.\n /* eslint-disable @typescript-eslint/no-require-imports */\n const commands: Record<string, (commandArgv: string[]) => Promise<void>> = {\n go: (a) => require('../commands/go').goCommand(a),\n 'go:docker': (a) => require('../commands/goDocker').goDockerCommand(a),\n 'go:dev': (a) => require('../commands/goDev').goDevCommand(a),\n 'go:dev:web': (a) => require('../commands/goDevWeb').goDevWebCommand(a),\n teardown: (a) => require('../commands/teardown').teardownCommand(a),\n synth: (a) => require('../commands/synth').synthCommand(a),\n prep: (a) => require('../commands/prep').prepCommand(a),\n publish: (a) => require('../commands/publish').publishCommand(a),\n 'publish:build': (a) => require('../commands/publish').publishBuildCommand(a),\n 'publish:upload': (a) => require('../commands/publish').publishUploadCommand(a),\n 'publish:deploy': (a) => require('../commands/publish').publishDeployCommand(a),\n hooks: (a) => require('../commands/hooks').hooksCommand(a),\n };\n /* eslint-enable @typescript-eslint/no-require-imports */\n\n const run = commands[command];\n\n if (!run) {\n console.error(`Unknown command: ${command}`);\n console.log(usage);\n process.exitCode = 1;\n return;\n }\n\n try {\n await run(commandArgv);\n } catch (error) {\n console.error(`qpq ${command} failed:`, error);\n process.exitCode = 1;\n }\n};\n"]}
1
+ {"version":3,"file":"runCli.js","sourceRoot":"","sources":["../../../src/cli/runCli.ts"],"names":[],"mappings":"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","sourcesContent":["import { promptCommandFromMenu } from './commandMenu';\nimport { buildUsage, CliCommand, cliCommands } from './commandRegistry';\n\n// Service infrastructure.ts files (and app config fragments) are TypeScript —\n// register ts-node's transpile-only require hook so the CLI can require them\n// directly, using the consumer root's tsconfig.json ts-node settings.\n//\n// CONTRACT: JavaScript apps (create-qpq-app --language javascript) also lean\n// on this hook — their tsconfig.json ts-node block sets allowJs (so their\n// ESM-syntax infrastructure.js compiles at require time) and scope/scopeDir\n// (so the hook never touches quidproquo libs outside the app). Any\n// replacement for ts-node here must honour those consumer tsconfig options.\nconst registerTsNode = (): void => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require('ts-node').register({ transpileOnly: true });\n};\n\n// Inquirer throws ExitPromptError on ctrl-c; treat that as a quiet exit\n// rather than a command failure.\nconst isPromptExit = (error: unknown): boolean => error instanceof Error && error.name === 'ExitPromptError';\n\n// Bare `qpq` on a terminal: drill through the command menu, gathering any\n// arguments the picked command needs. Returns null when the user bails out.\nconst resolveCommandInteractively = async (): Promise<{ command: CliCommand; commandArgv: string[] } | null> => {\n try {\n const command = await promptCommandFromMenu(cliCommands);\n const commandArgv = command.promptArgs ? await command.promptArgs() : [];\n\n return { command, commandArgv };\n } catch (error) {\n if (isPromptExit(error)) {\n return null;\n }\n\n throw error;\n }\n};\n\nexport const runCli = async (argv: string[]): Promise<void> => {\n const [commandName, ...argvRest] = argv;\n\n if (commandName === 'help' || commandName === '--help') {\n console.log(buildUsage());\n return;\n }\n\n let command: CliCommand | undefined;\n let commandArgv: string[] = [];\n\n if (!commandName) {\n // Only prompt on a real terminal; piped/CI invocations get the usage text.\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n console.log(buildUsage());\n return;\n }\n\n const resolved = await resolveCommandInteractively();\n\n if (!resolved) {\n return;\n }\n\n ({ command, commandArgv } = resolved);\n } else {\n command = cliCommands.find((c) => c.name === commandName);\n commandArgv = argvRest;\n\n if (!command) {\n console.error(`Unknown command: ${commandName}`);\n console.log(buildUsage());\n process.exitCode = 1;\n return;\n }\n }\n\n registerTsNode();\n\n try {\n await command.run(commandArgv);\n } catch (error) {\n console.error(`qpq ${command.name} failed:`, error);\n process.exitCode = 1;\n }\n};\n"]}
@@ -0,0 +1 @@
1
+ export declare const clearResourcesCommand: (argv: string[]) => Promise<void>;
@@ -0,0 +1,23 @@
1
+ // `qpq clear-resources` — interactively EMPTY selected data resources (storage
2
+ // drive buckets, key value store tables) without touching the stacks: the
3
+ // "reset my data" counterpart to `qpq teardown`. Resolves the app +
4
+ // environment the same way as go/teardown, enumerates the app's owned
5
+ // resources from its live qpq configs, multi-selects what to empty (with a
6
+ // typed confirmation — this deletes stored data), then hands off to the
7
+ // platform driver.
8
+ import { promptClearResourcesPlan } from '../lib/clearResourcesPlan';
9
+ import { resolveDeployEnvironment } from '../lib/deployEnv';
10
+ import { resolveAppSelection } from '../lib/resolveAppSelection';
11
+ import { getPlatformDriver } from '../platforms';
12
+ export const clearResourcesCommand = async (argv) => {
13
+ const appName = await resolveAppSelection({ argv, envVar: 'DEPLOY_APP_NAME' });
14
+ const { platform } = await resolveDeployEnvironment(argv, appName);
15
+ const driver = getPlatformDriver(platform);
16
+ if (!driver.clearResources) {
17
+ console.error(`The '${platform}' platform has no clear-resources strategy.`);
18
+ process.exit(1);
19
+ }
20
+ const plan = await promptClearResourcesPlan(appName);
21
+ await driver.clearResources(appName, plan);
22
+ };
23
+ //# sourceMappingURL=clearResources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clearResources.js","sourceRoot":"","sources":["../../../src/commands/clearResources.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,0EAA0E;AAC1E,oEAAoE;AACpE,sEAAsE;AACtE,2EAA2E;AAC3E,wEAAwE;AACxE,mBAAmB;AACnB,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,EAAE,IAAc,EAAiB,EAAE;IAC3E,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC/E,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,wBAAwB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAE3C,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QAC3B,OAAO,CAAC,KAAK,CAAC,QAAQ,QAAQ,6CAA6C,CAAC,CAAC;QAC7E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAErD,MAAM,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC,CAAC","sourcesContent":["// `qpq clear-resources` — interactively EMPTY selected data resources (storage\n// drive buckets, key value store tables) without touching the stacks: the\n// \"reset my data\" counterpart to `qpq teardown`. Resolves the app +\n// environment the same way as go/teardown, enumerates the app's owned\n// resources from its live qpq configs, multi-selects what to empty (with a\n// typed confirmation — this deletes stored data), then hands off to the\n// platform driver.\nimport { promptClearResourcesPlan } from '../lib/clearResourcesPlan';\nimport { resolveDeployEnvironment } from '../lib/deployEnv';\nimport { resolveAppSelection } from '../lib/resolveAppSelection';\nimport { getPlatformDriver } from '../platforms';\n\nexport const clearResourcesCommand = async (argv: string[]): Promise<void> => {\n const appName = await resolveAppSelection({ argv, envVar: 'DEPLOY_APP_NAME' });\n const { platform } = await resolveDeployEnvironment(argv, appName);\n const driver = getPlatformDriver(platform);\n\n if (!driver.clearResources) {\n console.error(`The '${platform}' platform has no clear-resources strategy.`);\n process.exit(1);\n }\n\n const plan = await promptClearResourcesPlan(appName);\n\n await driver.clearResources(appName, plan);\n};\n"]}
@@ -2,14 +2,15 @@
2
2
  // the deploy platform in apps/<app>/deploy.config.json, default aws), asks the
3
3
  // deploy plan, then hands off to the platform driver.
4
4
  import { resolveDeployEnvironment } from '../lib/deployEnv';
5
- import { promptDeployPlan } from '../lib/deployPrompts';
5
+ import { buildDeployPlanFromArgs, promptDeployPlan } from '../lib/deployPrompts';
6
6
  import { resolveAppSelection } from '../lib/resolveAppSelection';
7
7
  import { getPlatformDriver } from '../platforms';
8
8
  export const goCommand = async (argv) => {
9
9
  const appName = await resolveAppSelection({ argv, envVar: 'DEPLOY_APP_NAME' });
10
10
  const { platform } = await resolveDeployEnvironment(argv, appName);
11
11
  const driver = getPlatformDriver(platform);
12
- const plan = await promptDeployPlan(appName);
12
+ // Positional args (`qpq go all all`) run prompt-free; otherwise ask.
13
+ const plan = buildDeployPlanFromArgs(appName, argv) ?? (await promptDeployPlan(appName));
13
14
  await driver.go(appName, plan);
14
15
  };
15
16
  //# sourceMappingURL=go.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"go.js","sourceRoot":"","sources":["../../../src/commands/go.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,+EAA+E;AAC/E,sDAAsD;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAAE,IAAc,EAAiB,EAAE;IAC/D,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC/E,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,wBAAwB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAE3C,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAE7C,MAAM,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AACjC,CAAC,CAAC","sourcesContent":["// `qpq go` — interactive deploy. Resolves the app + environment (which names\n// the deploy platform in apps/<app>/deploy.config.json, default aws), asks the\n// deploy plan, then hands off to the platform driver.\nimport { resolveDeployEnvironment } from '../lib/deployEnv';\nimport { promptDeployPlan } from '../lib/deployPrompts';\nimport { resolveAppSelection } from '../lib/resolveAppSelection';\nimport { getPlatformDriver } from '../platforms';\n\nexport const goCommand = async (argv: string[]): Promise<void> => {\n const appName = await resolveAppSelection({ argv, envVar: 'DEPLOY_APP_NAME' });\n const { platform } = await resolveDeployEnvironment(argv, appName);\n const driver = getPlatformDriver(platform);\n\n const plan = await promptDeployPlan(appName);\n\n await driver.go(appName, plan);\n};\n"]}
1
+ {"version":3,"file":"go.js","sourceRoot":"","sources":["../../../src/commands/go.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,+EAA+E;AAC/E,sDAAsD;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAAE,IAAc,EAAiB,EAAE;IAC/D,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC/E,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,wBAAwB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAE3C,qEAAqE;IACrE,MAAM,IAAI,GAAG,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;IAEzF,MAAM,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AACjC,CAAC,CAAC","sourcesContent":["// `qpq go` — interactive deploy. Resolves the app + environment (which names\n// the deploy platform in apps/<app>/deploy.config.json, default aws), asks the\n// deploy plan, then hands off to the platform driver.\nimport { resolveDeployEnvironment } from '../lib/deployEnv';\nimport { buildDeployPlanFromArgs, promptDeployPlan } from '../lib/deployPrompts';\nimport { resolveAppSelection } from '../lib/resolveAppSelection';\nimport { getPlatformDriver } from '../platforms';\n\nexport const goCommand = async (argv: string[]): Promise<void> => {\n const appName = await resolveAppSelection({ argv, envVar: 'DEPLOY_APP_NAME' });\n const { platform } = await resolveDeployEnvironment(argv, appName);\n const driver = getPlatformDriver(platform);\n\n // Positional args (`qpq go all all`) run prompt-free; otherwise ask.\n const plan = buildDeployPlanFromArgs(appName, argv) ?? (await promptDeployPlan(appName));\n\n await driver.go(appName, plan);\n};\n"]}
@@ -1,127 +1,31 @@
1
- // `qpq go:dev` — boots the QPQ local dev server. Resolves the app, generates
2
- // the dev-server entry, builds the rspack config, then runs rspack in watch
3
- // mode, restarting the server process on every successful rebuild.
4
- //
5
- // The one thing NOT hot-reloaded is the qpq configs themselves: each service's
6
- // infrastructure.ts is evaluated once at launch and baked into the
7
- // `quidproquo-dynamic-loader` virtual module config changes need a full
8
- // `go:dev` restart.
9
- import { getAppServiceQpqConfigs, getDevServerRspackConfig } from 'quidproquo-deploy-rspack';
10
- import { execSync, spawn } from 'child_process';
11
- import path from 'path';
12
- import { rspack } from '@rspack/core';
13
- import { primeDeployEnvFromConfig } from '../lib/deployEnv';
14
- import { writeDevServerEntry } from '../lib/devServerEntry';
1
+ // `qpq go:dev` — the whole local dev stack in one process: the API dev server
2
+ // (go:dev:api) plus every views dev server (go:dev:web), so a single ctrl+c
3
+ // tears it all down. A background tsc watcher keeps workspace declaration
4
+ // output fresh for the editor (--no-types to skip it). The app is resolved
5
+ // once here and handed to both sub-commands via QPQ_DEV_APP; their output
6
+ // shares the terminal.
7
+ import { getAllViews } from 'quidproquo-deploy-rspack';
15
8
  import { getRoot } from '../lib/discovery';
16
9
  import { resolveAppSelection } from '../lib/resolveAppSelection';
17
- // 8080/8888 are set in the generated entry; 3001 is the quidproquo-dev-server
18
- // file-storage (secure URL) default port.
19
- const DEV_SERVER_PORTS = [8080, 8888, 3001];
20
- const DEV_SERVER_BUNDLE_PATH = path.join('dist', 'qpq', 'dev-server', 'main.js');
21
- // Pre-start sweep: a previous `go:dev` whose wrapper died without killing its
22
- // child (closed terminal, SIGKILL) leaves an orphaned server process holding
23
- // the dev ports, and the next launch dies with EADDRINUSE. Before starting,
24
- // kill any listener on those ports that is running the dev-server bundle.
25
- // Anything else on the ports is left alone and reported — it's not ours.
26
- const killStaleDevServers = () => {
27
- for (const port of DEV_SERVER_PORTS) {
28
- let pids = [];
29
- try {
30
- pids = execSync(`lsof -t -iTCP:${port} -sTCP:LISTEN`, {
31
- stdio: ['ignore', 'pipe', 'ignore'],
32
- })
33
- .toString()
34
- .split('\n')
35
- .map((line) => Number(line.trim()))
36
- .filter(Boolean);
37
- }
38
- catch {
39
- continue; // lsof exits non-zero when nothing is listening
40
- }
41
- for (const pid of pids) {
42
- let command = '';
43
- try {
44
- command = execSync(`ps -p ${pid} -o command=`).toString().trim();
45
- }
46
- catch {
47
- continue; // already gone
48
- }
49
- if (command.includes(DEV_SERVER_BUNDLE_PATH)) {
50
- console.log(`Killing stale dev server on port ${port} (pid ${pid})`);
51
- process.kill(pid);
52
- }
53
- else {
54
- console.warn(`Port ${port} is in use by an unrelated process (pid ${pid}: ${command}) — not killing it.`);
55
- }
56
- }
57
- }
58
- };
10
+ import { startTypeWatcher } from '../lib/typeWatcher';
11
+ import { goDevApiCommand } from './goDevApi';
12
+ import { goDevWebCommand } from './goDevWeb';
59
13
  export const goDevCommand = async (argv) => {
60
- const root = getRoot();
61
14
  const appName = await resolveAppSelection({ argv, envVar: 'QPQ_DEV_APP' });
62
15
  process.env.QPQ_DEV_APP = appName;
63
- primeDeployEnvFromConfig(appName);
64
- console.log(`Dev server for app [${appName}]`);
65
- killStaleDevServers();
66
- const qpqConfigs = getAppServiceQpqConfigs(root, appName);
67
- const entry = writeDevServerEntry(root, appName);
68
- const rspackConfig = getDevServerRspackConfig({ root, entry, qpqConfigs });
69
- const bundlePath = path.join(root, DEV_SERVER_BUNDLE_PATH);
70
- const env = { ...process.env, QPQ_DEV_APP: appName };
71
- // The long-lived server process (HTTP 8080 / WS 8888), restarted per rebuild.
72
- let child = null;
73
- let restartQueued = false;
74
- const startServer = () => {
75
- child = spawn('node', [bundlePath], { stdio: 'inherit', env });
76
- child.on('exit', (code) => {
77
- child = null;
78
- if (!restartQueued) {
79
- // Crashed (or exited) on its own — leave it down; the next successful
80
- // rebuild (i.e. the next file save) brings it back up.
81
- console.log(`Dev server exited with code ${code}. Save a file to restart it.`);
82
- }
83
- });
84
- };
85
- const restartServer = () => {
86
- if (restartQueued)
87
- return;
88
- if (!child) {
89
- startServer();
90
- return;
91
- }
92
- restartQueued = true;
93
- child.once('exit', () => {
94
- restartQueued = false;
95
- startServer();
96
- });
97
- child.kill();
98
- };
99
- console.log('Bundling dev server (watch mode)...');
100
- let lastHash;
101
- const compiler = rspack(rspackConfig);
102
- compiler.watch({ aggregateTimeout: 200 }, (err, stats) => {
103
- if (err) {
104
- console.error(err);
105
- return;
106
- }
107
- if (stats?.hasErrors()) {
108
- console.error(stats.toString({ colors: true, chunks: false, modules: false }));
109
- console.log('Build failed — dev server not restarted. Fix the error and save again.');
110
- return;
111
- }
112
- // Skip no-op rebuilds (e.g. the virtual-module-triggered second compile at
113
- // startup): only restart when the output actually changed.
114
- if (stats?.hash === lastHash && child) {
115
- return;
116
- }
117
- lastHash = stats?.hash;
118
- console.log(child ? 'Rebuilt. Restarting dev server...' : 'Dev server bundled. Starting...');
119
- restartServer();
120
- });
121
- process.on('SIGINT', () => {
122
- restartQueued = true; // suppress the crash log / rebuild-restart on our own kill
123
- child?.kill();
124
- compiler.close(() => process.exit(0));
125
- });
16
+ // Editor/type freshness only: the rspack builds below bundle workspace
17
+ // source directly, so the dev loop itself never waits on tsc.
18
+ const typeWatcher = argv.includes('--no-types') ? null : startTypeWatcher(getRoot());
19
+ if (typeWatcher) {
20
+ process.on('exit', () => typeWatcher.kill());
21
+ }
22
+ // Kicks off the rspack watch and returns; the API server keeps running in
23
+ // the background of this process.
24
+ await goDevApiCommand(argv);
25
+ if (getAllViews(getRoot(), appName).length === 0) {
26
+ console.log('No views projects found. Running the API dev server only.');
27
+ return;
28
+ }
29
+ await goDevWebCommand(argv, { plainStatusLines: true });
126
30
  };
127
31
  //# sourceMappingURL=goDev.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"goDev.js","sourceRoot":"","sources":["../../../src/commands/goDev.ts"],"names":[],"mappings":"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","sourcesContent":["// `qpq go:dev` — boots the QPQ local dev server. Resolves the app, generates\n// the dev-server entry, builds the rspack config, then runs rspack in watch\n// mode, restarting the server process on every successful rebuild.\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` restart.\nimport { getAppServiceQpqConfigs, getDevServerRspackConfig } from 'quidproquo-deploy-rspack';\n\nimport { ChildProcess, execSync, 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 { 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\n// Pre-start sweep: a previous `go:dev` whose wrapper died without killing its\n// child (closed terminal, SIGKILL) leaves an orphaned server process holding\n// the dev ports, and the next launch dies with EADDRINUSE. Before starting,\n// kill any listener on those ports that is running the dev-server bundle.\n// Anything else on the ports is left alone and reported — it's not ours.\nconst killStaleDevServers = (): void => {\n for (const port of DEV_SERVER_PORTS) {\n let pids: number[] = [];\n try {\n pids = execSync(`lsof -t -iTCP:${port} -sTCP:LISTEN`, {\n stdio: ['ignore', 'pipe', 'ignore'],\n })\n .toString()\n .split('\\n')\n .map((line) => Number(line.trim()))\n .filter(Boolean);\n } catch {\n continue; // lsof exits non-zero when nothing is listening\n }\n\n for (const pid of pids) {\n let command = '';\n try {\n command = execSync(`ps -p ${pid} -o command=`).toString().trim();\n } catch {\n continue; // already gone\n }\n if (command.includes(DEV_SERVER_BUNDLE_PATH)) {\n console.log(`Killing stale dev server on port ${port} (pid ${pid})`);\n process.kill(pid);\n } else {\n console.warn(`Port ${port} is in use by an unrelated process (pid ${pid}: ${command}) — not killing it.`);\n }\n }\n }\n};\n\nexport const goDevCommand = 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 killStaleDevServers();\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 child.kill();\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 process.on('SIGINT', () => {\n restartQueued = true; // suppress the crash log / rebuild-restart on our own kill\n child?.kill();\n compiler.close(() => process.exit(0));\n });\n};\n"]}
1
+ {"version":3,"file":"goDev.js","sourceRoot":"","sources":["../../../src/commands/goDev.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,4EAA4E;AAC5E,0EAA0E;AAC1E,2EAA2E;AAC3E,0EAA0E;AAC1E,uBAAuB;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,IAAc,EAAiB,EAAE;IAClE,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;IAC3E,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,OAAO,CAAC;IAElC,uEAAuE;IACvE,8DAA8D;IAC9D,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC;IACrF,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,0EAA0E;IAC1E,kCAAkC;IAClC,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IAE5B,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;QACzE,OAAO;IACT,CAAC;IAED,MAAM,eAAe,CAAC,IAAI,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;AAC1D,CAAC,CAAC","sourcesContent":["// `qpq go:dev` — the whole local dev stack in one process: the API dev server\n// (go:dev:api) plus every views dev server (go:dev:web), so a single ctrl+c\n// tears it all down. A background tsc watcher keeps workspace declaration\n// output fresh for the editor (--no-types to skip it). The app is resolved\n// once here and handed to both sub-commands via QPQ_DEV_APP; their output\n// shares the terminal.\nimport { getAllViews } from 'quidproquo-deploy-rspack';\n\nimport { getRoot } from '../lib/discovery';\nimport { resolveAppSelection } from '../lib/resolveAppSelection';\nimport { startTypeWatcher } from '../lib/typeWatcher';\nimport { goDevApiCommand } from './goDevApi';\nimport { goDevWebCommand } from './goDevWeb';\n\nexport const goDevCommand = async (argv: string[]): Promise<void> => {\n const appName = await resolveAppSelection({ argv, envVar: 'QPQ_DEV_APP' });\n process.env.QPQ_DEV_APP = appName;\n\n // Editor/type freshness only: the rspack builds below bundle workspace\n // source directly, so the dev loop itself never waits on tsc.\n const typeWatcher = argv.includes('--no-types') ? null : startTypeWatcher(getRoot());\n if (typeWatcher) {\n process.on('exit', () => typeWatcher.kill());\n }\n\n // Kicks off the rspack watch and returns; the API server keeps running in\n // the background of this process.\n await goDevApiCommand(argv);\n\n if (getAllViews(getRoot(), appName).length === 0) {\n console.log('No views projects found. Running the API dev server only.');\n return;\n }\n\n await goDevWebCommand(argv, { plainStatusLines: true });\n};\n"]}
@@ -0,0 +1 @@
1
+ export declare const goDevApiCommand: (argv: string[]) => Promise<void>;