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
@@ -8,136 +8,37 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
11
  Object.defineProperty(exports, "__esModule", { value: true });
15
12
  exports.goDevCommand = void 0;
16
- // `qpq go:dev` — boots the QPQ local dev server. Resolves the app, generates
17
- // the dev-server entry, builds the rspack config, then runs rspack in watch
18
- // mode, restarting the server process on every successful rebuild.
19
- //
20
- // The one thing NOT hot-reloaded is the qpq configs themselves: each service's
21
- // infrastructure.ts is evaluated once at launch and baked into the
22
- // `quidproquo-dynamic-loader` virtual module — config changes need a full
23
- // `go:dev` restart.
13
+ // `qpq go:dev` — the whole local dev stack in one process: the API dev server
14
+ // (go:dev:api) plus every views dev server (go:dev:web), so a single ctrl+c
15
+ // tears it all down. A background tsc watcher keeps workspace declaration
16
+ // output fresh for the editor (--no-types to skip it). The app is resolved
17
+ // once here and handed to both sub-commands via QPQ_DEV_APP; their output
18
+ // shares the terminal.
24
19
  const quidproquo_deploy_rspack_1 = require("quidproquo-deploy-rspack");
25
- const child_process_1 = require("child_process");
26
- const path_1 = __importDefault(require("path"));
27
- const core_1 = require("@rspack/core");
28
- const deployEnv_1 = require("../lib/deployEnv");
29
- const devServerEntry_1 = require("../lib/devServerEntry");
30
20
  const discovery_1 = require("../lib/discovery");
31
21
  const resolveAppSelection_1 = require("../lib/resolveAppSelection");
32
- // 8080/8888 are set in the generated entry; 3001 is the quidproquo-dev-server
33
- // file-storage (secure URL) default port.
34
- const DEV_SERVER_PORTS = [8080, 8888, 3001];
35
- const DEV_SERVER_BUNDLE_PATH = path_1.default.join('dist', 'qpq', 'dev-server', 'main.js');
36
- // Pre-start sweep: a previous `go:dev` whose wrapper died without killing its
37
- // child (closed terminal, SIGKILL) leaves an orphaned server process holding
38
- // the dev ports, and the next launch dies with EADDRINUSE. Before starting,
39
- // kill any listener on those ports that is running the dev-server bundle.
40
- // Anything else on the ports is left alone and reported — it's not ours.
41
- const killStaleDevServers = () => {
42
- for (const port of DEV_SERVER_PORTS) {
43
- let pids = [];
44
- try {
45
- pids = (0, child_process_1.execSync)(`lsof -t -iTCP:${port} -sTCP:LISTEN`, {
46
- stdio: ['ignore', 'pipe', 'ignore'],
47
- })
48
- .toString()
49
- .split('\n')
50
- .map((line) => Number(line.trim()))
51
- .filter(Boolean);
52
- }
53
- catch (_a) {
54
- continue; // lsof exits non-zero when nothing is listening
55
- }
56
- for (const pid of pids) {
57
- let command = '';
58
- try {
59
- command = (0, child_process_1.execSync)(`ps -p ${pid} -o command=`).toString().trim();
60
- }
61
- catch (_b) {
62
- continue; // already gone
63
- }
64
- if (command.includes(DEV_SERVER_BUNDLE_PATH)) {
65
- console.log(`Killing stale dev server on port ${port} (pid ${pid})`);
66
- process.kill(pid);
67
- }
68
- else {
69
- console.warn(`Port ${port} is in use by an unrelated process (pid ${pid}: ${command}) — not killing it.`);
70
- }
71
- }
72
- }
73
- };
22
+ const typeWatcher_1 = require("../lib/typeWatcher");
23
+ const goDevApi_1 = require("./goDevApi");
24
+ const goDevWeb_1 = require("./goDevWeb");
74
25
  const goDevCommand = (argv) => __awaiter(void 0, void 0, void 0, function* () {
75
- const root = (0, discovery_1.getRoot)();
76
26
  const appName = yield (0, resolveAppSelection_1.resolveAppSelection)({ argv, envVar: 'QPQ_DEV_APP' });
77
27
  process.env.QPQ_DEV_APP = appName;
78
- (0, deployEnv_1.primeDeployEnvFromConfig)(appName);
79
- console.log(`Dev server for app [${appName}]`);
80
- killStaleDevServers();
81
- const qpqConfigs = (0, quidproquo_deploy_rspack_1.getAppServiceQpqConfigs)(root, appName);
82
- const entry = (0, devServerEntry_1.writeDevServerEntry)(root, appName);
83
- const rspackConfig = (0, quidproquo_deploy_rspack_1.getDevServerRspackConfig)({ root, entry, qpqConfigs });
84
- const bundlePath = path_1.default.join(root, DEV_SERVER_BUNDLE_PATH);
85
- const env = Object.assign(Object.assign({}, process.env), { QPQ_DEV_APP: appName });
86
- // The long-lived server process (HTTP 8080 / WS 8888), restarted per rebuild.
87
- let child = null;
88
- let restartQueued = false;
89
- const startServer = () => {
90
- child = (0, child_process_1.spawn)('node', [bundlePath], { stdio: 'inherit', env });
91
- child.on('exit', (code) => {
92
- child = null;
93
- if (!restartQueued) {
94
- // Crashed (or exited) on its own — leave it down; the next successful
95
- // rebuild (i.e. the next file save) brings it back up.
96
- console.log(`Dev server exited with code ${code}. Save a file to restart it.`);
97
- }
98
- });
99
- };
100
- const restartServer = () => {
101
- if (restartQueued)
102
- return;
103
- if (!child) {
104
- startServer();
105
- return;
106
- }
107
- restartQueued = true;
108
- child.once('exit', () => {
109
- restartQueued = false;
110
- startServer();
111
- });
112
- child.kill();
113
- };
114
- console.log('Bundling dev server (watch mode)...');
115
- let lastHash;
116
- const compiler = (0, core_1.rspack)(rspackConfig);
117
- compiler.watch({ aggregateTimeout: 200 }, (err, stats) => {
118
- if (err) {
119
- console.error(err);
120
- return;
121
- }
122
- if (stats === null || stats === void 0 ? void 0 : stats.hasErrors()) {
123
- console.error(stats.toString({ colors: true, chunks: false, modules: false }));
124
- console.log('Build failed — dev server not restarted. Fix the error and save again.');
125
- return;
126
- }
127
- // Skip no-op rebuilds (e.g. the virtual-module-triggered second compile at
128
- // startup): only restart when the output actually changed.
129
- if ((stats === null || stats === void 0 ? void 0 : stats.hash) === lastHash && child) {
130
- return;
131
- }
132
- lastHash = stats === null || stats === void 0 ? void 0 : stats.hash;
133
- console.log(child ? 'Rebuilt. Restarting dev server...' : 'Dev server bundled. Starting...');
134
- restartServer();
135
- });
136
- process.on('SIGINT', () => {
137
- restartQueued = true; // suppress the crash log / rebuild-restart on our own kill
138
- child === null || child === void 0 ? void 0 : child.kill();
139
- compiler.close(() => process.exit(0));
140
- });
28
+ // Editor/type freshness only: the rspack builds below bundle workspace
29
+ // source directly, so the dev loop itself never waits on tsc.
30
+ const typeWatcher = argv.includes('--no-types') ? null : (0, typeWatcher_1.startTypeWatcher)((0, discovery_1.getRoot)());
31
+ if (typeWatcher) {
32
+ process.on('exit', () => typeWatcher.kill());
33
+ }
34
+ // Kicks off the rspack watch and returns; the API server keeps running in
35
+ // the background of this process.
36
+ yield (0, goDevApi_1.goDevApiCommand)(argv);
37
+ if ((0, quidproquo_deploy_rspack_1.getAllViews)((0, discovery_1.getRoot)(), appName).length === 0) {
38
+ console.log('No views projects found. Running the API dev server only.');
39
+ return;
40
+ }
41
+ yield (0, goDevWeb_1.goDevWebCommand)(argv, { plainStatusLines: true });
141
42
  });
142
43
  exports.goDevCommand = goDevCommand;
143
44
  //# 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,uEAAuD;AAEvD,gDAA2C;AAC3C,oEAAiE;AACjE,oDAAsD;AACtD,yCAA6C;AAC7C,yCAA6C;AAEtC,MAAM,YAAY,GAAG,CAAO,IAAc,EAAiB,EAAE;IAClE,MAAM,OAAO,GAAG,MAAM,IAAA,yCAAmB,EAAC,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,IAAA,8BAAgB,EAAC,IAAA,mBAAO,GAAE,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,IAAA,0BAAe,EAAC,IAAI,CAAC,CAAC;IAE5B,IAAI,IAAA,sCAAW,EAAC,IAAA,mBAAO,GAAE,EAAE,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;QACzE,OAAO;IACT,CAAC;IAED,MAAM,IAAA,0BAAe,EAAC,IAAI,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;AAC1D,CAAC,CAAA,CAAC;AArBW,QAAA,YAAY,gBAqBvB","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>;
@@ -0,0 +1,110 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.goDevApiCommand = void 0;
16
+ // `qpq go:dev:api` — boots the QPQ local dev server. Resolves the app,
17
+ // generates the dev-server entry, builds the rspack config, then runs rspack
18
+ // in watch mode, restarting the server process on every successful rebuild.
19
+ //
20
+ // The one thing NOT hot-reloaded is the qpq configs themselves: each service's
21
+ // infrastructure.ts is evaluated once at launch and baked into the
22
+ // `quidproquo-dynamic-loader` virtual module — config changes need a full
23
+ // `go:dev:api` restart.
24
+ const quidproquo_deploy_rspack_1 = require("quidproquo-deploy-rspack");
25
+ const child_process_1 = require("child_process");
26
+ const path_1 = __importDefault(require("path"));
27
+ const core_1 = require("@rspack/core");
28
+ const deployEnv_1 = require("../lib/deployEnv");
29
+ const devServerEntry_1 = require("../lib/devServerEntry");
30
+ const discovery_1 = require("../lib/discovery");
31
+ const killStaleListeners_1 = require("../lib/killStaleListeners");
32
+ const resolveAppSelection_1 = require("../lib/resolveAppSelection");
33
+ // 8080/8888 are set in the generated entry; 3001 is the quidproquo-dev-server
34
+ // file-storage (secure URL) default port.
35
+ const DEV_SERVER_PORTS = [8080, 8888, 3001];
36
+ const DEV_SERVER_BUNDLE_PATH = path_1.default.join('dist', 'qpq', 'dev-server', 'main.js');
37
+ const goDevApiCommand = (argv) => __awaiter(void 0, void 0, void 0, function* () {
38
+ const root = (0, discovery_1.getRoot)();
39
+ const appName = yield (0, resolveAppSelection_1.resolveAppSelection)({ argv, envVar: 'QPQ_DEV_APP' });
40
+ process.env.QPQ_DEV_APP = appName;
41
+ (0, deployEnv_1.primeDeployEnvFromConfig)(appName);
42
+ console.log(`Dev server for app [${appName}]`);
43
+ // Catches a lingering watcher from a previous run even if its spawned
44
+ // child already exited (see killOtherQpqDevProcesses) — then the usual
45
+ // port-based sweep for anything else still bound to our ports.
46
+ (0, killStaleListeners_1.killOtherQpqDevProcesses)(root);
47
+ (0, killStaleListeners_1.killStaleListeners)(DEV_SERVER_PORTS, (command) => command.includes(DEV_SERVER_BUNDLE_PATH));
48
+ const qpqConfigs = (0, quidproquo_deploy_rspack_1.getAppServiceQpqConfigs)(root, appName);
49
+ const entry = (0, devServerEntry_1.writeDevServerEntry)(root, appName);
50
+ const rspackConfig = (0, quidproquo_deploy_rspack_1.getDevServerRspackConfig)({ root, entry, qpqConfigs });
51
+ const bundlePath = path_1.default.join(root, DEV_SERVER_BUNDLE_PATH);
52
+ const env = Object.assign(Object.assign({}, process.env), { QPQ_DEV_APP: appName });
53
+ // The long-lived server process (HTTP 8080 / WS 8888), restarted per rebuild.
54
+ let child = null;
55
+ let restartQueued = false;
56
+ const startServer = () => {
57
+ child = (0, child_process_1.spawn)('node', [bundlePath], { stdio: 'inherit', env });
58
+ child.on('exit', (code) => {
59
+ child = null;
60
+ if (!restartQueued) {
61
+ // Crashed (or exited) on its own — leave it down; the next successful
62
+ // rebuild (i.e. the next file save) brings it back up.
63
+ console.log(`Dev server exited with code ${code}. Save a file to restart it.`);
64
+ }
65
+ });
66
+ };
67
+ const restartServer = () => {
68
+ if (restartQueued)
69
+ return;
70
+ if (!child) {
71
+ startServer();
72
+ return;
73
+ }
74
+ restartQueued = true;
75
+ child.once('exit', () => {
76
+ restartQueued = false;
77
+ startServer();
78
+ });
79
+ child.kill();
80
+ };
81
+ console.log('Bundling dev server (watch mode)...');
82
+ let lastHash;
83
+ const compiler = (0, core_1.rspack)(rspackConfig);
84
+ compiler.watch({ aggregateTimeout: 200 }, (err, stats) => {
85
+ if (err) {
86
+ console.error(err);
87
+ return;
88
+ }
89
+ if (stats === null || stats === void 0 ? void 0 : stats.hasErrors()) {
90
+ console.error(stats.toString({ colors: true, chunks: false, modules: false }));
91
+ console.log('Build failed — dev server not restarted. Fix the error and save again.');
92
+ return;
93
+ }
94
+ // Skip no-op rebuilds (e.g. the virtual-module-triggered second compile at
95
+ // startup): only restart when the output actually changed.
96
+ if ((stats === null || stats === void 0 ? void 0 : stats.hash) === lastHash && child) {
97
+ return;
98
+ }
99
+ lastHash = stats === null || stats === void 0 ? void 0 : stats.hash;
100
+ console.log(child ? 'Rebuilt. Restarting dev server...' : 'Dev server bundled. Starting...');
101
+ restartServer();
102
+ });
103
+ process.on('SIGINT', () => {
104
+ restartQueued = true; // suppress the crash log / rebuild-restart on our own kill
105
+ child === null || child === void 0 ? void 0 : child.kill();
106
+ compiler.close(() => process.exit(0));
107
+ });
108
+ });
109
+ exports.goDevApiCommand = goDevApiCommand;
110
+ //# sourceMappingURL=goDevApi.js.map
@@ -0,0 +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// 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 { 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 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 +1,4 @@
1
- export declare const goDevWebCommand: (argv: string[]) => Promise<void>;
1
+ export type GoDevWebOptions = {
2
+ plainStatusLines?: boolean;
3
+ };
4
+ export declare const goDevWebCommand: (argv: string[], options?: GoDevWebOptions) => Promise<void>;
@@ -30,8 +30,9 @@ const dev_server_1 = require("@rspack/dev-server");
30
30
  const args_1 = require("../lib/args");
31
31
  const deployEnv_1 = require("../lib/deployEnv");
32
32
  const discovery_1 = require("../lib/discovery");
33
+ const killStaleListeners_1 = require("../lib/killStaleListeners");
33
34
  const resolveAppSelection_1 = require("../lib/resolveAppSelection");
34
- const goDevWebCommand = (argv) => __awaiter(void 0, void 0, void 0, function* () {
35
+ const goDevWebCommand = (argv_1, ...args_2) => __awaiter(void 0, [argv_1, ...args_2], void 0, function* (argv, options = {}) {
35
36
  var _a;
36
37
  const root = (0, discovery_1.getRoot)();
37
38
  const appName = yield (0, resolveAppSelection_1.resolveAppSelection)({ argv, envVar: 'QPQ_DEV_APP' });
@@ -46,6 +47,7 @@ const goDevWebCommand = (argv) => __awaiter(void 0, void 0, void 0, function* ()
46
47
  console.error('No views projects found.');
47
48
  process.exit(1);
48
49
  }
50
+ (0, killStaleListeners_1.killStaleListeners)(views.map((v) => v.port), killStaleListeners_1.isQpqCliCommand);
49
51
  // Every RspackDevServer.start() registers its own SIGINT/SIGTERM handler on
50
52
  // process — with a dozen views servers in one process that trips node's
51
53
  // default 10-listener warning. Size the limit to the fleet.
@@ -74,7 +76,7 @@ const goDevWebCommand = (argv) => __awaiter(void 0, void 0, void 0, function* ()
74
76
  };
75
77
  const markStarted = (index) => {
76
78
  const v = views[index];
77
- if (!process.stdout.isTTY) {
79
+ if (!process.stdout.isTTY || options.plainStatusLines) {
78
80
  logBelowList(` ${v.service} started`);
79
81
  return;
80
82
  }
@@ -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 { resolveAppSelection } from '../lib/resolveAppSelection';\n\nexport const goDevWebCommand = async (argv: string[]): 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 // 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 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 { 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, so fall back to plain lines.\n plainStatusLines?: 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 || options.plainStatusLines) {\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 pollUntilStarted(index);\n }\n\n process.on('SIGINT', () => shutdown(0));\n process.on('SIGTERM', () => shutdown(0));\n};\n"]}
@@ -18,6 +18,7 @@ const deployPrompts_1 = require("../lib/deployPrompts");
18
18
  const resolveAppSelection_1 = require("../lib/resolveAppSelection");
19
19
  const platforms_1 = require("../platforms");
20
20
  const goDockerCommand = (argv) => __awaiter(void 0, void 0, void 0, function* () {
21
+ var _a;
21
22
  const appName = yield (0, resolveAppSelection_1.resolveAppSelection)({ argv, envVar: 'DEPLOY_APP_NAME' });
22
23
  const { platform } = yield (0, deployEnv_1.resolveDeployEnvironment)(argv, appName);
23
24
  const driver = (0, platforms_1.getPlatformDriver)(platform);
@@ -25,7 +26,8 @@ const goDockerCommand = (argv) => __awaiter(void 0, void 0, void 0, function* ()
25
26
  console.error(`Platform '${platform}' has no dockerized deploy — use qpq go instead.`);
26
27
  process.exit(1);
27
28
  }
28
- const plan = yield (0, deployPrompts_1.promptDeployPlan)(appName);
29
+ // Positional args (`qpq go:docker all all`) run prompt-free; otherwise ask.
30
+ const plan = (_a = (0, deployPrompts_1.buildDeployPlanFromArgs)(appName, argv)) !== null && _a !== void 0 ? _a : (yield (0, deployPrompts_1.promptDeployPlan)(appName));
29
31
  yield driver.goDocker(appName, plan);
30
32
  });
31
33
  exports.goDockerCommand = goDockerCommand;
@@ -1 +1 @@
1
- {"version":3,"file":"goDocker.js","sourceRoot":"","sources":["../../../src/commands/goDocker.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uEAAuE;AACvE,4EAA4E;AAC5E,6BAA6B;AAC7B,gDAA4D;AAC5D,wDAAwD;AACxD,oEAAiE;AACjE,4CAAiD;AAE1C,MAAM,eAAe,GAAG,CAAO,IAAc,EAAiB,EAAE;IACrE,MAAM,OAAO,GAAG,MAAM,IAAA,yCAAmB,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC/E,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAA,oCAAwB,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,IAAA,6BAAiB,EAAC,QAAQ,CAAC,CAAC;IAE3C,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrB,OAAO,CAAC,KAAK,CAAC,aAAa,QAAQ,kDAAkD,CAAC,CAAC;QACvF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,IAAA,gCAAgB,EAAC,OAAO,CAAC,CAAC;IAE7C,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC,CAAA,CAAC;AAbW,QAAA,eAAe,mBAa1B","sourcesContent":["// `qpq go:docker` — asks the same questions as `qpq go`, then runs the\n// platform driver's parallel containerized deploy (drivers without a docker\n// strategy don't offer one).\nimport { resolveDeployEnvironment } from '../lib/deployEnv';\nimport { promptDeployPlan } from '../lib/deployPrompts';\nimport { resolveAppSelection } from '../lib/resolveAppSelection';\nimport { getPlatformDriver } from '../platforms';\n\nexport const goDockerCommand = 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.goDocker) {\n console.error(`Platform '${platform}' has no dockerized deploy — use qpq go instead.`);\n process.exit(1);\n }\n\n const plan = await promptDeployPlan(appName);\n\n await driver.goDocker(appName, plan);\n};\n"]}
1
+ {"version":3,"file":"goDocker.js","sourceRoot":"","sources":["../../../src/commands/goDocker.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,uEAAuE;AACvE,4EAA4E;AAC5E,6BAA6B;AAC7B,gDAA4D;AAC5D,wDAAiF;AACjF,oEAAiE;AACjE,4CAAiD;AAE1C,MAAM,eAAe,GAAG,CAAO,IAAc,EAAiB,EAAE;;IACrE,MAAM,OAAO,GAAG,MAAM,IAAA,yCAAmB,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC/E,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAA,oCAAwB,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,IAAA,6BAAiB,EAAC,QAAQ,CAAC,CAAC;IAE3C,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrB,OAAO,CAAC,KAAK,CAAC,aAAa,QAAQ,kDAAkD,CAAC,CAAC;QACvF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,4EAA4E;IAC5E,MAAM,IAAI,GAAG,MAAA,IAAA,uCAAuB,EAAC,OAAO,EAAE,IAAI,CAAC,mCAAI,CAAC,MAAM,IAAA,gCAAgB,EAAC,OAAO,CAAC,CAAC,CAAC;IAEzF,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC,CAAA,CAAC;AAdW,QAAA,eAAe,mBAc1B","sourcesContent":["// `qpq go:docker` — asks the same questions as `qpq go`, then runs the\n// platform driver's parallel containerized deploy (drivers without a docker\n// strategy don't offer one).\nimport { resolveDeployEnvironment } from '../lib/deployEnv';\nimport { buildDeployPlanFromArgs, promptDeployPlan } from '../lib/deployPrompts';\nimport { resolveAppSelection } from '../lib/resolveAppSelection';\nimport { getPlatformDriver } from '../platforms';\n\nexport const goDockerCommand = 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.goDocker) {\n console.error(`Platform '${platform}' has no dockerized deploy — use qpq go instead.`);\n process.exit(1);\n }\n\n // Positional args (`qpq go:docker all all`) run prompt-free; otherwise ask.\n const plan = buildDeployPlanFromArgs(appName, argv) ?? (await promptDeployPlan(appName));\n\n await driver.goDocker(appName, plan);\n};\n"]}
@@ -8,19 +8,37 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
15
  exports.hooksCommand = void 0;
13
- // `qpq hooks <name>` — run the `qpq:<name>` script of every app that defines
14
- // one (apps/<app>/package.json). Wire the root package.json's lifecycle to it,
15
- // e.g. "postinstall": "qpq hooks postinstall".
16
+ // `qpq hooks <name>` — run the `qpq:<name>` script (or the plain `<name>`
17
+ // script when no qpq:-prefixed one exists) of every app and workspace package
18
+ // that defines one, in dependency order with parallel execution.
19
+ // Wire the root package.json's lifecycle to it, e.g.
20
+ // "postinstall": "qpq hooks postinstall". Use --jobs=1 to force serial runs.
21
+ const os_1 = __importDefault(require("os"));
22
+ const args_1 = require("../lib/args");
16
23
  const hooks_1 = require("../lib/hooks");
24
+ const runTasks_1 = require("../lib/runTasks");
25
+ // Leave a core for the rest of the machine, capped so a wide dependency level
26
+ // doesn't stack up too many concurrent npm processes.
27
+ const getJobs = (argv) => {
28
+ const parsed = Number((0, args_1.getArgValue)(argv, '--jobs'));
29
+ if (Number.isFinite(parsed) && parsed >= 1) {
30
+ return Math.floor(parsed);
31
+ }
32
+ return Math.max(1, Math.min(8, os_1.default.availableParallelism() - 1));
33
+ };
17
34
  const hooksCommand = (argv) => __awaiter(void 0, void 0, void 0, function* () {
18
35
  const [hook] = argv.filter((a) => !a.startsWith('--'));
19
36
  if (!hook) {
20
- console.error('Usage: qpq hooks <name> (runs qpq:<name> in every app that defines it)');
37
+ console.error('Usage: qpq hooks <name> [--jobs=N] (runs qpq:<name>, or plain <name>, in every app/package that defines it)');
21
38
  process.exit(1);
22
39
  }
23
- yield (0, hooks_1.runAppHookForAllApps)(hook);
40
+ const failures = yield (0, hooks_1.runHookForAllPackages)(hook, getJobs(argv));
41
+ (0, runTasks_1.assertNoFailures)(`hooks ${hook}`, failures);
24
42
  });
25
43
  exports.hooksCommand = hooksCommand;
26
44
  //# sourceMappingURL=hooks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../src/commands/hooks.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6EAA6E;AAC7E,+EAA+E;AAC/E,+CAA+C;AAC/C,wCAAoD;AAE7C,MAAM,YAAY,GAAG,CAAO,IAAc,EAAiB,EAAE;IAClE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAEvD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,0EAA0E,CAAC,CAAC;QAC1F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,IAAA,4BAAoB,EAAC,IAAI,CAAC,CAAC;AACnC,CAAC,CAAA,CAAC;AATW,QAAA,YAAY,gBASvB","sourcesContent":["// `qpq hooks <name>` — run the `qpq:<name>` script of every app that defines\n// one (apps/<app>/package.json). Wire the root package.json's lifecycle to it,\n// e.g. \"postinstall\": \"qpq hooks postinstall\".\nimport { runAppHookForAllApps } from '../lib/hooks';\n\nexport const hooksCommand = async (argv: string[]): Promise<void> => {\n const [hook] = argv.filter((a) => !a.startsWith('--'));\n\n if (!hook) {\n console.error('Usage: qpq hooks <name> (runs qpq:<name> in every app that defines it)');\n process.exit(1);\n }\n\n await runAppHookForAllApps(hook);\n};\n"]}
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../src/commands/hooks.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,0EAA0E;AAC1E,8EAA8E;AAC9E,iEAAiE;AACjE,qDAAqD;AACrD,6EAA6E;AAC7E,4CAAoB;AAEpB,sCAA0C;AAC1C,wCAAqD;AACrD,8CAAmD;AAEnD,8EAA8E;AAC9E,sDAAsD;AACtD,MAAM,OAAO,GAAG,CAAC,IAAc,EAAU,EAAE;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAA,kBAAW,EAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IACnD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAE,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;AACjE,CAAC,CAAC;AAEK,MAAM,YAAY,GAAG,CAAO,IAAc,EAAiB,EAAE;IAClE,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAEvD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,KAAK,CAAC,+GAA+G,CAAC,CAAC;QAC/H,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,IAAA,6BAAqB,EAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,IAAA,2BAAgB,EAAC,SAAS,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC9C,CAAC,CAAA,CAAC;AAVW,QAAA,YAAY,gBAUvB","sourcesContent":["// `qpq hooks <name>` — run the `qpq:<name>` script (or the plain `<name>`\n// script when no qpq:-prefixed one exists) of every app and workspace package\n// that defines one, in dependency order with parallel execution.\n// Wire the root package.json's lifecycle to it, e.g.\n// \"postinstall\": \"qpq hooks postinstall\". Use --jobs=1 to force serial runs.\nimport os from 'os';\n\nimport { getArgValue } from '../lib/args';\nimport { runHookForAllPackages } from '../lib/hooks';\nimport { assertNoFailures } from '../lib/runTasks';\n\n// Leave a core for the rest of the machine, capped so a wide dependency level\n// doesn't stack up too many concurrent npm processes.\nconst getJobs = (argv: string[]): number => {\n const parsed = Number(getArgValue(argv, '--jobs'));\n if (Number.isFinite(parsed) && parsed >= 1) {\n return Math.floor(parsed);\n }\n return Math.max(1, Math.min(8, os.availableParallelism() - 1));\n};\n\nexport const hooksCommand = async (argv: string[]): Promise<void> => {\n const [hook] = argv.filter((a) => !a.startsWith('--'));\n\n if (!hook) {\n console.error('Usage: qpq hooks <name> [--jobs=N] (runs qpq:<name>, or plain <name>, in every app/package that defines it)');\n process.exit(1);\n }\n\n const failures = await runHookForAllPackages(hook, getJobs(argv));\n assertNoFailures(`hooks ${hook}`, failures);\n};\n"]}
@@ -0,0 +1,15 @@
1
+ export type ClearableResourceKind = 'storageDrive' | 'keyValueStore';
2
+ export type ClearableResource = {
3
+ service: string;
4
+ kind: ClearableResourceKind;
5
+ resourceName: string;
6
+ };
7
+ export type ClearResourcesPlan = {
8
+ kind: 'cancelled';
9
+ } | {
10
+ kind: 'resources';
11
+ appName: string;
12
+ resources: ClearableResource[];
13
+ };
14
+ export declare const getClearableResources: (appName: string) => ClearableResource[];
15
+ export declare const promptClearResourcesPlan: (appName: string) => Promise<ClearResourcesPlan>;
@@ -0,0 +1,80 @@
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.promptClearResourcesPlan = exports.getClearableResources = void 0;
13
+ // The question flow for `qpq clear-resources`: enumerate every service's OWNED
14
+ // data resources (storage drives -> buckets, key value stores -> tables) from
15
+ // its live qpq config, multi-select which to empty, then type the deploy
16
+ // target back to confirm - emptying deletes stored data, so a y/n prompt
17
+ // isn't enough friction. Owned-only so a cross-module ref (e.g. a membership
18
+ // table every service declares against one owner) appears exactly once, on
19
+ // the service that owns it.
20
+ const quidproquo_core_1 = require("quidproquo-core");
21
+ const discovery_1 = require("./discovery");
22
+ const prompts_1 = require("./prompts");
23
+ const qpqConfigs_1 = require("./qpqConfigs");
24
+ // Every owned data resource across the app's services, in service order.
25
+ const getClearableResources = (appName) => {
26
+ const resources = [];
27
+ for (const service of (0, discovery_1.getServiceNames)(appName)) {
28
+ const qpqConfig = (0, qpqConfigs_1.loadServiceQpqConfig)(appName, service);
29
+ for (const drive of quidproquo_core_1.qpqCoreUtils.getOwnedStorageDrives(qpqConfig)) {
30
+ resources.push({ service, kind: 'storageDrive', resourceName: drive.storageDrive });
31
+ }
32
+ for (const store of quidproquo_core_1.qpqCoreUtils.getOwnedKeyValueStores(qpqConfig)) {
33
+ resources.push({ service, kind: 'keyValueStore', resourceName: store.keyValueStoreName });
34
+ }
35
+ }
36
+ return resources;
37
+ };
38
+ exports.getClearableResources = getClearableResources;
39
+ const BY_SERVICE = 'By service — empty everything the selected services own';
40
+ const BY_RESOURCE = 'By resource — pick individual buckets/tables';
41
+ // Service mode: pick services, take every resource each one owns.
42
+ const pickByService = (clearable) => __awaiter(void 0, void 0, void 0, function* () {
43
+ const services = [...new Set(clearable.map((resource) => resource.service))];
44
+ const countFor = (service) => clearable.filter((resource) => resource.service === service).length;
45
+ const picked = yield (0, prompts_1.promptCheckbox)('Select services to EMPTY (every bucket/table the service owns)', services.map((service) => ({ name: `${service} (${countFor(service)} resources)`, value: service })));
46
+ return clearable.filter((resource) => picked.includes(resource.service));
47
+ });
48
+ // Resource mode: pick individual buckets/tables.
49
+ const pickByResource = (clearable) => (0, prompts_1.promptCheckbox)('Select resources to EMPTY (all stored data will be deleted)', clearable.map((resource) => ({
50
+ name: `${resource.service} — ${resource.resourceName} (${resource.kind === 'storageDrive' ? 'bucket' : 'table'})`,
51
+ value: resource,
52
+ })));
53
+ const promptClearResourcesPlan = (appName) => __awaiter(void 0, void 0, void 0, function* () {
54
+ const deployInfo = process.env.ACTOR_NAME
55
+ ? `${appName}-${process.env.ENVIRONMENT}-${process.env.ACTOR_NAME}`
56
+ : `${appName}-${process.env.ENVIRONMENT}`;
57
+ const clearable = (0, exports.getClearableResources)(appName);
58
+ if (clearable.length === 0) {
59
+ console.log('No storage drives or key value stores found in this app.');
60
+ return { kind: 'cancelled' };
61
+ }
62
+ const mode = yield (0, prompts_1.promptSelect)('How do you want to select what to empty?', [BY_SERVICE, BY_RESOURCE]);
63
+ const resources = mode === BY_SERVICE ? yield pickByService(clearable) : yield pickByResource(clearable);
64
+ if (resources.length === 0) {
65
+ console.log('Nothing selected. Exiting!');
66
+ return { kind: 'cancelled' };
67
+ }
68
+ console.log(`\nThis will EMPTY (delete all stored data from) ${resources.length} resource(s) in [${deployInfo}]:`);
69
+ for (const resource of resources) {
70
+ console.log(` ${resource.service} — ${resource.resourceName} (${resource.kind === 'storageDrive' ? 'bucket' : 'table'})`);
71
+ }
72
+ const confirmation = yield (0, prompts_1.promptText)(`Type '${deployInfo}' to confirm`);
73
+ if (confirmation !== deployInfo) {
74
+ console.log('Confirmation did not match. Exiting!');
75
+ return { kind: 'cancelled' };
76
+ }
77
+ return { kind: 'resources', appName, resources };
78
+ });
79
+ exports.promptClearResourcesPlan = promptClearResourcesPlan;
80
+ //# sourceMappingURL=clearResourcesPlan.js.map