@qpqjs/cli 0.2.1 → 0.4.0

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 (284) hide show
  1. package/lib/commonjs/cli/commandMenu.js +5 -4
  2. package/lib/commonjs/cli/commandMenu.js.map +1 -1
  3. package/lib/commonjs/cli/runCli.js +15 -7
  4. package/lib/commonjs/cli/runCli.js.map +1 -1
  5. package/lib/commonjs/commands/checkCircular.js +15 -7
  6. package/lib/commonjs/commands/checkCircular.js.map +1 -1
  7. package/lib/commonjs/commands/clearResources.js +8 -14
  8. package/lib/commonjs/commands/clearResources.js.map +1 -1
  9. package/lib/commonjs/commands/go.js +3 -7
  10. package/lib/commonjs/commands/go.js.map +1 -1
  11. package/lib/commonjs/commands/goDev.js +4 -2
  12. package/lib/commonjs/commands/goDev.js.map +1 -1
  13. package/lib/commonjs/commands/goDevApi.js +23 -20
  14. package/lib/commonjs/commands/goDevApi.js.map +1 -1
  15. package/lib/commonjs/commands/goDevWeb.js +13 -8
  16. package/lib/commonjs/commands/goDevWeb.js.map +1 -1
  17. package/lib/commonjs/commands/goDocker.js +10 -13
  18. package/lib/commonjs/commands/goDocker.js.map +1 -1
  19. package/lib/commonjs/commands/hooks.js +4 -2
  20. package/lib/commonjs/commands/hooks.js.map +1 -1
  21. package/lib/commonjs/commands/migrate.js +11 -9
  22. package/lib/commonjs/commands/migrate.js.map +1 -1
  23. package/lib/commonjs/commands/prep.js +4 -2
  24. package/lib/commonjs/commands/prep.js.map +1 -1
  25. package/lib/commonjs/commands/publish.js +4 -7
  26. package/lib/commonjs/commands/publish.js.map +1 -1
  27. package/lib/commonjs/commands/scriptBuild.js +7 -5
  28. package/lib/commonjs/commands/scriptBuild.js.map +1 -1
  29. package/lib/commonjs/commands/scriptBuilderLayer.js +6 -4
  30. package/lib/commonjs/commands/scriptBuilderLayer.js.map +1 -1
  31. package/lib/commonjs/commands/scriptNew.js +7 -5
  32. package/lib/commonjs/commands/scriptNew.js.map +1 -1
  33. package/lib/commonjs/commands/setup.js +14 -22
  34. package/lib/commonjs/commands/setup.js.map +1 -1
  35. package/lib/commonjs/commands/synth.js +16 -20
  36. package/lib/commonjs/commands/synth.js.map +1 -1
  37. package/lib/commonjs/commands/teardown.js +8 -14
  38. package/lib/commonjs/commands/teardown.js.map +1 -1
  39. package/lib/commonjs/lib/CliError.d.ts +4 -0
  40. package/lib/commonjs/lib/CliError.js +14 -0
  41. package/lib/commonjs/lib/CliError.js.map +1 -0
  42. package/lib/commonjs/lib/circularImports.d.ts +0 -1
  43. package/lib/commonjs/lib/circularImports.js +5 -6
  44. package/lib/commonjs/lib/circularImports.js.map +1 -1
  45. package/lib/commonjs/lib/clearResourcesPlan.d.ts +0 -1
  46. package/lib/commonjs/lib/clearResourcesPlan.js +12 -13
  47. package/lib/commonjs/lib/clearResourcesPlan.js.map +1 -1
  48. package/lib/commonjs/lib/deployPrompts.d.ts +2 -1
  49. package/lib/commonjs/lib/deployPrompts.js +16 -16
  50. package/lib/commonjs/lib/deployPrompts.js.map +1 -1
  51. package/lib/commonjs/lib/devServerEntry.js +5 -4
  52. package/lib/commonjs/lib/devServerEntry.js.map +1 -1
  53. package/lib/commonjs/lib/devServerPorts.js +2 -4
  54. package/lib/commonjs/lib/devServerPorts.js.map +1 -1
  55. package/lib/commonjs/lib/discovery.d.ts +0 -2
  56. package/lib/commonjs/lib/discovery.js +11 -44
  57. package/lib/commonjs/lib/discovery.js.map +1 -1
  58. package/lib/commonjs/lib/hooks.js +5 -3
  59. package/lib/commonjs/lib/hooks.js.map +1 -1
  60. package/lib/commonjs/lib/killChildWithEscalation.js +3 -1
  61. package/lib/commonjs/lib/killChildWithEscalation.js.map +1 -1
  62. package/lib/commonjs/lib/killStaleListeners.js +24 -37
  63. package/lib/commonjs/lib/killStaleListeners.js.map +1 -1
  64. package/lib/commonjs/lib/out.d.ts +11 -0
  65. package/lib/commonjs/lib/out.js +30 -0
  66. package/lib/commonjs/lib/out.js.map +1 -0
  67. package/lib/commonjs/lib/prompts.d.ts +1 -0
  68. package/lib/commonjs/lib/prompts.js +15 -2
  69. package/lib/commonjs/lib/prompts.js.map +1 -1
  70. package/lib/commonjs/lib/qpqConfigs.js +4 -4
  71. package/lib/commonjs/lib/qpqConfigs.js.map +1 -1
  72. package/lib/commonjs/lib/requireDriverCapability.d.ts +7 -0
  73. package/lib/commonjs/lib/requireDriverCapability.js +17 -0
  74. package/lib/commonjs/lib/requireDriverCapability.js.map +1 -0
  75. package/lib/commonjs/lib/resolveAppSelection.d.ts +1 -0
  76. package/lib/commonjs/lib/resolveAppSelection.js +19 -12
  77. package/lib/commonjs/lib/resolveAppSelection.js.map +1 -1
  78. package/lib/commonjs/lib/resolveCommandContext.d.ts +12 -0
  79. package/lib/commonjs/lib/resolveCommandContext.js +26 -0
  80. package/lib/commonjs/lib/resolveCommandContext.js.map +1 -0
  81. package/lib/commonjs/lib/resolveDeployment.d.ts +0 -1
  82. package/lib/commonjs/lib/resolveDeployment.js +15 -10
  83. package/lib/commonjs/lib/resolveDeployment.js.map +1 -1
  84. package/lib/commonjs/lib/runCommand.d.ts +20 -0
  85. package/lib/commonjs/lib/runCommand.js +64 -34
  86. package/lib/commonjs/lib/runCommand.js.map +1 -1
  87. package/lib/commonjs/lib/runTasks.js +8 -4
  88. package/lib/commonjs/lib/runTasks.js.map +1 -1
  89. package/lib/commonjs/lib/timing.js +5 -3
  90. package/lib/commonjs/lib/timing.js.map +1 -1
  91. package/lib/commonjs/lib/typeWatcher.d.ts +2 -1
  92. package/lib/commonjs/lib/typeWatcher.js +6 -13
  93. package/lib/commonjs/lib/typeWatcher.js.map +1 -1
  94. package/lib/commonjs/lib/userScriptBuilderLayer.d.ts +0 -2
  95. package/lib/commonjs/lib/userScriptBuilderLayer.js +7 -8
  96. package/lib/commonjs/lib/userScriptBuilderLayer.js.map +1 -1
  97. package/lib/commonjs/lib/views.js +3 -2
  98. package/lib/commonjs/lib/views.js.map +1 -1
  99. package/lib/commonjs/lib/webAddressingEnv.d.ts +0 -2
  100. package/lib/commonjs/lib/webAddressingEnv.js +4 -4
  101. package/lib/commonjs/lib/webAddressingEnv.js.map +1 -1
  102. package/lib/commonjs/platforms/aws/awsCredentials.js +3 -1
  103. package/lib/commonjs/platforms/aws/awsCredentials.js.map +1 -1
  104. package/lib/commonjs/platforms/aws/cdkApp.js +1 -1
  105. package/lib/commonjs/platforms/aws/cdkApp.js.map +1 -1
  106. package/lib/commonjs/platforms/aws/clearResources.js +16 -26
  107. package/lib/commonjs/platforms/aws/clearResources.js.map +1 -1
  108. package/lib/commonjs/platforms/aws/getDeployEnv.d.ts +1 -0
  109. package/lib/commonjs/platforms/aws/getDeployEnv.js +12 -0
  110. package/lib/commonjs/platforms/aws/getDeployEnv.js.map +1 -0
  111. package/lib/commonjs/platforms/aws/go.js +17 -15
  112. package/lib/commonjs/platforms/aws/go.js.map +1 -1
  113. package/lib/commonjs/platforms/aws/goDocker.js +29 -26
  114. package/lib/commonjs/platforms/aws/goDocker.js.map +1 -1
  115. package/lib/commonjs/platforms/aws/goDockerManifest.js +6 -1
  116. package/lib/commonjs/platforms/aws/goDockerManifest.js.map +1 -1
  117. package/lib/commonjs/platforms/aws/publish.js +13 -11
  118. package/lib/commonjs/platforms/aws/publish.js.map +1 -1
  119. package/lib/commonjs/platforms/aws/remote.js +8 -7
  120. package/lib/commonjs/platforms/aws/remote.js.map +1 -1
  121. package/lib/commonjs/platforms/aws/setup/buildBucketNamesStep.js +4 -4
  122. package/lib/commonjs/platforms/aws/setup/buildBucketNamesStep.js.map +1 -1
  123. package/lib/commonjs/platforms/aws/setup/buildGithubDeployRoleStep.js +4 -3
  124. package/lib/commonjs/platforms/aws/setup/buildGithubDeployRoleStep.js.map +1 -1
  125. package/lib/commonjs/platforms/aws/stacks.d.ts +0 -1
  126. package/lib/commonjs/platforms/aws/stacks.js +21 -33
  127. package/lib/commonjs/platforms/aws/stacks.js.map +1 -1
  128. package/lib/commonjs/platforms/aws/teardown.js +9 -7
  129. package/lib/commonjs/platforms/aws/teardown.js.map +1 -1
  130. package/lib/commonjs/platforms/aws/viewsSync.js +23 -11
  131. package/lib/commonjs/platforms/aws/viewsSync.js.map +1 -1
  132. package/lib/commonjs/platforms/docker/go.js +20 -18
  133. package/lib/commonjs/platforms/docker/go.js.map +1 -1
  134. package/lib/commonjs/platforms/docker/index.js +5 -4
  135. package/lib/commonjs/platforms/docker/index.js.map +1 -1
  136. package/lib/commonjs/platforms/docker/parseMounts.js +3 -2
  137. package/lib/commonjs/platforms/docker/parseMounts.js.map +1 -1
  138. package/lib/commonjs/platforms/index.js +4 -2
  139. package/lib/commonjs/platforms/index.js.map +1 -1
  140. package/lib/commonjs/platforms/types.d.ts +2 -2
  141. package/lib/commonjs/platforms/types.js.map +1 -1
  142. package/lib/esm/cli/commandMenu.js +5 -4
  143. package/lib/esm/cli/commandMenu.js.map +1 -1
  144. package/lib/esm/cli/runCli.js +15 -7
  145. package/lib/esm/cli/runCli.js.map +1 -1
  146. package/lib/esm/commands/checkCircular.js +15 -7
  147. package/lib/esm/commands/checkCircular.js.map +1 -1
  148. package/lib/esm/commands/clearResources.js +8 -14
  149. package/lib/esm/commands/clearResources.js.map +1 -1
  150. package/lib/esm/commands/go.js +3 -7
  151. package/lib/esm/commands/go.js.map +1 -1
  152. package/lib/esm/commands/goDev.js +4 -2
  153. package/lib/esm/commands/goDev.js.map +1 -1
  154. package/lib/esm/commands/goDevApi.js +21 -18
  155. package/lib/esm/commands/goDevApi.js.map +1 -1
  156. package/lib/esm/commands/goDevWeb.js +12 -8
  157. package/lib/esm/commands/goDevWeb.js.map +1 -1
  158. package/lib/esm/commands/goDocker.js +10 -13
  159. package/lib/esm/commands/goDocker.js.map +1 -1
  160. package/lib/esm/commands/hooks.js +4 -2
  161. package/lib/esm/commands/hooks.js.map +1 -1
  162. package/lib/esm/commands/migrate.js +9 -7
  163. package/lib/esm/commands/migrate.js.map +1 -1
  164. package/lib/esm/commands/prep.js +4 -2
  165. package/lib/esm/commands/prep.js.map +1 -1
  166. package/lib/esm/commands/publish.js +4 -7
  167. package/lib/esm/commands/publish.js.map +1 -1
  168. package/lib/esm/commands/scriptBuild.js +7 -5
  169. package/lib/esm/commands/scriptBuild.js.map +1 -1
  170. package/lib/esm/commands/scriptBuilderLayer.js +6 -4
  171. package/lib/esm/commands/scriptBuilderLayer.js.map +1 -1
  172. package/lib/esm/commands/scriptNew.js +7 -5
  173. package/lib/esm/commands/scriptNew.js.map +1 -1
  174. package/lib/esm/commands/setup.js +14 -22
  175. package/lib/esm/commands/setup.js.map +1 -1
  176. package/lib/esm/commands/synth.js +18 -22
  177. package/lib/esm/commands/synth.js.map +1 -1
  178. package/lib/esm/commands/teardown.js +8 -14
  179. package/lib/esm/commands/teardown.js.map +1 -1
  180. package/lib/esm/lib/CliError.d.ts +4 -0
  181. package/lib/esm/lib/CliError.js +11 -0
  182. package/lib/esm/lib/CliError.js.map +1 -0
  183. package/lib/esm/lib/circularImports.d.ts +0 -1
  184. package/lib/esm/lib/circularImports.js +4 -4
  185. package/lib/esm/lib/circularImports.js.map +1 -1
  186. package/lib/esm/lib/clearResourcesPlan.d.ts +0 -1
  187. package/lib/esm/lib/clearResourcesPlan.js +12 -12
  188. package/lib/esm/lib/clearResourcesPlan.js.map +1 -1
  189. package/lib/esm/lib/deployPrompts.d.ts +2 -1
  190. package/lib/esm/lib/deployPrompts.js +17 -17
  191. package/lib/esm/lib/deployPrompts.js.map +1 -1
  192. package/lib/esm/lib/devServerEntry.js +5 -4
  193. package/lib/esm/lib/devServerEntry.js.map +1 -1
  194. package/lib/esm/lib/devServerPorts.js +2 -3
  195. package/lib/esm/lib/devServerPorts.js.map +1 -1
  196. package/lib/esm/lib/discovery.d.ts +0 -2
  197. package/lib/esm/lib/discovery.js +10 -38
  198. package/lib/esm/lib/discovery.js.map +1 -1
  199. package/lib/esm/lib/hooks.js +5 -3
  200. package/lib/esm/lib/hooks.js.map +1 -1
  201. package/lib/esm/lib/killChildWithEscalation.js +3 -1
  202. package/lib/esm/lib/killChildWithEscalation.js.map +1 -1
  203. package/lib/esm/lib/killStaleListeners.js +24 -37
  204. package/lib/esm/lib/killStaleListeners.js.map +1 -1
  205. package/lib/esm/lib/out.d.ts +11 -0
  206. package/lib/esm/lib/out.js +26 -0
  207. package/lib/esm/lib/out.js.map +1 -0
  208. package/lib/esm/lib/prompts.d.ts +1 -0
  209. package/lib/esm/lib/prompts.js +13 -1
  210. package/lib/esm/lib/prompts.js.map +1 -1
  211. package/lib/esm/lib/qpqConfigs.js +4 -4
  212. package/lib/esm/lib/qpqConfigs.js.map +1 -1
  213. package/lib/esm/lib/requireDriverCapability.d.ts +7 -0
  214. package/lib/esm/lib/requireDriverCapability.js +13 -0
  215. package/lib/esm/lib/requireDriverCapability.js.map +1 -0
  216. package/lib/esm/lib/resolveAppSelection.d.ts +1 -0
  217. package/lib/esm/lib/resolveAppSelection.js +17 -11
  218. package/lib/esm/lib/resolveAppSelection.js.map +1 -1
  219. package/lib/esm/lib/resolveCommandContext.d.ts +12 -0
  220. package/lib/esm/lib/resolveCommandContext.js +12 -0
  221. package/lib/esm/lib/resolveCommandContext.js.map +1 -0
  222. package/lib/esm/lib/resolveDeployment.d.ts +0 -1
  223. package/lib/esm/lib/resolveDeployment.js +13 -8
  224. package/lib/esm/lib/resolveDeployment.js.map +1 -1
  225. package/lib/esm/lib/runCommand.d.ts +20 -0
  226. package/lib/esm/lib/runCommand.js +52 -31
  227. package/lib/esm/lib/runCommand.js.map +1 -1
  228. package/lib/esm/lib/runTasks.js +8 -4
  229. package/lib/esm/lib/runTasks.js.map +1 -1
  230. package/lib/esm/lib/timing.js +5 -3
  231. package/lib/esm/lib/timing.js.map +1 -1
  232. package/lib/esm/lib/typeWatcher.d.ts +2 -1
  233. package/lib/esm/lib/typeWatcher.js +6 -13
  234. package/lib/esm/lib/typeWatcher.js.map +1 -1
  235. package/lib/esm/lib/userScriptBuilderLayer.d.ts +0 -2
  236. package/lib/esm/lib/userScriptBuilderLayer.js +5 -5
  237. package/lib/esm/lib/userScriptBuilderLayer.js.map +1 -1
  238. package/lib/esm/lib/views.js +3 -2
  239. package/lib/esm/lib/views.js.map +1 -1
  240. package/lib/esm/lib/webAddressingEnv.d.ts +0 -2
  241. package/lib/esm/lib/webAddressingEnv.js +1 -1
  242. package/lib/esm/lib/webAddressingEnv.js.map +1 -1
  243. package/lib/esm/platforms/aws/awsCredentials.js +3 -1
  244. package/lib/esm/platforms/aws/awsCredentials.js.map +1 -1
  245. package/lib/esm/platforms/aws/cdkApp.js +1 -1
  246. package/lib/esm/platforms/aws/cdkApp.js.map +1 -1
  247. package/lib/esm/platforms/aws/clearResources.js +16 -25
  248. package/lib/esm/platforms/aws/clearResources.js.map +1 -1
  249. package/lib/esm/platforms/aws/getDeployEnv.d.ts +1 -0
  250. package/lib/esm/platforms/aws/getDeployEnv.js +8 -0
  251. package/lib/esm/platforms/aws/getDeployEnv.js.map +1 -0
  252. package/lib/esm/platforms/aws/go.js +17 -15
  253. package/lib/esm/platforms/aws/go.js.map +1 -1
  254. package/lib/esm/platforms/aws/goDocker.js +29 -26
  255. package/lib/esm/platforms/aws/goDocker.js.map +1 -1
  256. package/lib/esm/platforms/aws/goDockerManifest.js +6 -1
  257. package/lib/esm/platforms/aws/goDockerManifest.js.map +1 -1
  258. package/lib/esm/platforms/aws/publish.js +13 -11
  259. package/lib/esm/platforms/aws/publish.js.map +1 -1
  260. package/lib/esm/platforms/aws/remote.js +9 -7
  261. package/lib/esm/platforms/aws/remote.js.map +1 -1
  262. package/lib/esm/platforms/aws/setup/buildBucketNamesStep.js +2 -2
  263. package/lib/esm/platforms/aws/setup/buildBucketNamesStep.js.map +1 -1
  264. package/lib/esm/platforms/aws/setup/buildGithubDeployRoleStep.js +4 -3
  265. package/lib/esm/platforms/aws/setup/buildGithubDeployRoleStep.js.map +1 -1
  266. package/lib/esm/platforms/aws/stacks.d.ts +0 -1
  267. package/lib/esm/platforms/aws/stacks.js +22 -30
  268. package/lib/esm/platforms/aws/stacks.js.map +1 -1
  269. package/lib/esm/platforms/aws/teardown.js +9 -7
  270. package/lib/esm/platforms/aws/teardown.js.map +1 -1
  271. package/lib/esm/platforms/aws/viewsSync.js +22 -10
  272. package/lib/esm/platforms/aws/viewsSync.js.map +1 -1
  273. package/lib/esm/platforms/docker/go.js +21 -19
  274. package/lib/esm/platforms/docker/go.js.map +1 -1
  275. package/lib/esm/platforms/docker/index.js +5 -4
  276. package/lib/esm/platforms/docker/index.js.map +1 -1
  277. package/lib/esm/platforms/docker/parseMounts.js +3 -2
  278. package/lib/esm/platforms/docker/parseMounts.js.map +1 -1
  279. package/lib/esm/platforms/index.js +4 -2
  280. package/lib/esm/platforms/index.js.map +1 -1
  281. package/lib/esm/platforms/types.d.ts +2 -2
  282. package/lib/esm/platforms/types.js.map +1 -1
  283. package/package.json +28 -15
  284. package/templates/user-script/README.md +1 -1
@@ -10,14 +10,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.promptTeardownPlan = exports.promptDeployPlan = exports.buildDeployPlanFromArgs = void 0;
13
- // The interactive question flow shared by `qpq go` (sequential AWS deploy) and
14
- // a future dockerized variant: pick an app, confirm the target, pick services
15
- // (or domain/bootstrap), pick stacks. buildDeployPlanFromArgs is the
16
- // non-interactive shortcut (`qpq go all all`) that skips every prompt.
17
13
  const args_1 = require("./args");
18
- const getDeployTargetLabel_1 = require("./getDeployTargetLabel");
19
14
  const discovery_1 = require("./discovery");
15
+ const getDeployTargetLabel_1 = require("./getDeployTargetLabel");
20
16
  const prompts_1 = require("./prompts");
17
+ const CliError_1 = require("./CliError");
18
+ const out_1 = require("./out");
19
+ const out = (0, out_1.createOutput)('deploy-prompts');
21
20
  const STACK_TYPES = ['all', 'inf', 'api', 'web', 'views'];
22
21
  // Non-interactive deploy plan from positional args: `qpq go <services> [stacks]`.
23
22
  // services: 'all' | comma-separated service names | 'account' | 'domain' | 'bootstrap'
@@ -40,16 +39,19 @@ const buildDeployPlanFromArgs = (appName, argv) => {
40
39
  return { kind: 'bootstrap', appName, includeDomain: false };
41
40
  }
42
41
  const allServices = (0, discovery_1.getServiceNames)(appName);
43
- const services = servicesArg === 'all' ? allServices : servicesArg.split(',').map((s) => s.trim()).filter(Boolean);
42
+ const services = servicesArg === 'all'
43
+ ? allServices
44
+ : servicesArg
45
+ .split(',')
46
+ .map((s) => s.trim())
47
+ .filter(Boolean);
44
48
  const unknownServices = services.filter((service) => !allServices.includes(service));
45
49
  if (unknownServices.length > 0) {
46
- console.error(`Unknown service(s): ${unknownServices.join(', ')}. Valid services: ${allServices.join(', ')}`);
47
- process.exit(1);
50
+ throw new CliError_1.CliError(`Unknown service(s): ${unknownServices.join(', ')}. Valid services: ${allServices.join(', ')}`);
48
51
  }
49
52
  const stacks = (stacksArg !== null && stacksArg !== void 0 ? stacksArg : 'all');
50
53
  if (!STACK_TYPES.includes(stacks)) {
51
- console.error(`Unknown stacks '${stacksArg}'. Valid stacks: ${STACK_TYPES.join(', ')}`);
52
- process.exit(1);
54
+ throw new CliError_1.CliError(`Unknown stacks '${stacksArg}'. Valid stacks: ${STACK_TYPES.join(', ')}`);
53
55
  }
54
56
  return { kind: 'services', appName, services: [...new Set(services)], stacks };
55
57
  };
@@ -57,7 +59,7 @@ exports.buildDeployPlanFromArgs = buildDeployPlanFromArgs;
57
59
  const promptDeployPlan = (appName) => __awaiter(void 0, void 0, void 0, function* () {
58
60
  const deployInfo = (0, getDeployTargetLabel_1.getDeployTargetLabel)();
59
61
  if (!(yield (0, prompts_1.promptYesNo)(`Deploying to: [${deployInfo}]`))) {
60
- console.log('Exiting!');
62
+ out.log('Exiting!');
61
63
  return { kind: 'cancelled' };
62
64
  }
63
65
  let services = [];
@@ -90,7 +92,7 @@ const promptDeployPlan = (appName) => __awaiter(void 0, void 0, void 0, function
90
92
  });
91
93
  exports.promptDeployPlan = promptDeployPlan;
92
94
  // The question flow for `qpq teardown`: pick services, then type the deploy
93
- // target back to confirm — destroying inf stacks takes user data (key value
95
+ // target back to confirm, destroying inf stacks takes user data (key value
94
96
  // stores, user directories) with it, so a y/n prompt isn't enough friction.
95
97
  const promptTeardownPlan = (appName) => __awaiter(void 0, void 0, void 0, function* () {
96
98
  const deployInfo = (0, getDeployTargetLabel_1.getDeployTargetLabel)();
@@ -106,10 +108,8 @@ const promptTeardownPlan = (appName) => __awaiter(void 0, void 0, void 0, functi
106
108
  services.push(service);
107
109
  } while (yield (0, prompts_1.promptYesNo)('Do you want to tear down another service?'));
108
110
  services = [...new Set(services)];
109
- console.log(`\nThis will DESTROY the web, api and inf stacks (including all stored data) for: ${services.join(', ')}`);
110
- const confirmation = yield (0, prompts_1.promptText)(`Type '${deployInfo}' to confirm`);
111
- if (confirmation !== deployInfo) {
112
- console.log('Confirmation did not match. Exiting!');
111
+ out.log(`\nThis will DESTROY the web, api and inf stacks (including all stored data) for: ${services.join(', ')}`);
112
+ if (!(yield (0, prompts_1.promptTypedConfirmation)(deployInfo))) {
113
113
  return { kind: 'cancelled' };
114
114
  }
115
115
  return { kind: 'services', appName, services };
@@ -1 +1 @@
1
- {"version":3,"file":"deployPrompts.js","sourceRoot":"","sources":["../../../src/lib/deployPrompts.ts"],"names":[],"mappings":"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","sourcesContent":["// The interactive question flow shared by `qpq go` (sequential AWS deploy) and\n// a future dockerized variant: pick an app, confirm the target, pick services\n// (or domain/bootstrap), pick stacks. buildDeployPlanFromArgs is the\n// non-interactive shortcut (`qpq go all all`) that skips every prompt.\nimport { getPositionalArgs } from './args';\nimport { getDeployTargetLabel } from './getDeployTargetLabel';\nimport { getServiceNames } from './discovery';\nimport { promptSelect, promptText, promptYesNo } from './prompts';\n\nexport type StackType = 'all' | 'inf' | 'api' | 'web' | 'views';\n\nconst STACK_TYPES: StackType[] = ['all', 'inf', 'api', 'web', 'views'];\n\nexport type DeployPlan =\n | { kind: 'cancelled' }\n // Deploy only the account-level guardrails stack (one per AWS account).\n | { kind: 'account'; appName: string }\n // Deploy the bootstrap stack (and optionally the domain stack first).\n | { kind: 'bootstrap'; appName: string; includeDomain: boolean }\n // Deploy the selected stacks for the selected services.\n | {\n kind: 'services';\n appName: string;\n services: string[];\n stacks: StackType;\n };\n\n// Non-interactive deploy plan from positional args: `qpq go <services> [stacks]`.\n// services: 'all' | comma-separated service names | 'account' | 'domain' | 'bootstrap'\n// stacks: all | inf | api | web | views (defaults to 'all')\n// Returns null when no positional services token is given, so callers fall back\n// to the interactive promptDeployPlan. Passing args is an explicit opt-in to a\n// prompt-free deploy, so the \"Deploying to: [env]\" confirmation is skipped too.\nexport const buildDeployPlanFromArgs = (appName: string, argv: string[]): DeployPlan | null => {\n const [servicesArg, stacksArg] = getPositionalArgs(argv, ['--app', '--deployment']);\n\n if (!servicesArg) {\n return null;\n }\n\n if (servicesArg === 'account') {\n return { kind: 'account', appName };\n }\n if (servicesArg === 'domain') {\n return { kind: 'bootstrap', appName, includeDomain: true };\n }\n if (servicesArg === 'bootstrap') {\n return { kind: 'bootstrap', appName, includeDomain: false };\n }\n\n const allServices = getServiceNames(appName);\n const services = servicesArg === 'all' ? allServices : servicesArg.split(',').map((s) => s.trim()).filter(Boolean);\n\n const unknownServices = services.filter((service) => !allServices.includes(service));\n if (unknownServices.length > 0) {\n console.error(`Unknown service(s): ${unknownServices.join(', ')}. Valid services: ${allServices.join(', ')}`);\n process.exit(1);\n }\n\n const stacks = (stacksArg ?? 'all') as StackType;\n if (!STACK_TYPES.includes(stacks)) {\n console.error(`Unknown stacks '${stacksArg}'. Valid stacks: ${STACK_TYPES.join(', ')}`);\n process.exit(1);\n }\n\n return { kind: 'services', appName, services: [...new Set(services)], stacks };\n};\n\nexport const promptDeployPlan = async (appName: string): Promise<DeployPlan> => {\n const deployInfo = getDeployTargetLabel();\n\n if (!(await promptYesNo(`Deploying to: [${deployInfo}]`))) {\n console.log('Exiting!');\n return { kind: 'cancelled' };\n }\n\n let services: string[] = [];\n\n do {\n const allServices = getServiceNames(appName);\n\n // Bracketed so the meta options can't be mistaken for actual services.\n const allText = '[ all ]';\n const accountText = '[ account ]';\n const domainEnvText = '[ domain ]';\n const bootstrapEnvText = '[ bootstrap environment ]';\n\n const service = await promptSelect('Select services to deploy', [allText, ...allServices, accountText, domainEnvText, bootstrapEnvText]);\n\n if (service === allText) {\n services = allServices;\n break;\n }\n if (service === accountText) {\n return { kind: 'account', appName };\n }\n if (service === domainEnvText) {\n return { kind: 'bootstrap', appName, includeDomain: true };\n }\n if (service === bootstrapEnvText) {\n return { kind: 'bootstrap', appName, includeDomain: false };\n }\n\n services.push(service);\n } while (await promptYesNo('Do you want to deploy another service?'));\n\n services = [...new Set(services)];\n\n const stacks = (await promptSelect('Stacks to deploy', ['all', 'inf', 'api', 'web', 'views'])) as StackType;\n\n return { kind: 'services', appName, services, stacks };\n};\n\nexport type TeardownPlan =\n | { kind: 'cancelled' }\n // Destroy the web, api and inf stacks for the selected services.\n | { kind: 'services'; appName: string; services: string[] };\n\n// The question flow for `qpq teardown`: pick services, then type the deploy\n// target back to confirm — destroying inf stacks takes user data (key value\n// stores, user directories) with it, so a y/n prompt isn't enough friction.\nexport const promptTeardownPlan = async (appName: string): Promise<TeardownPlan> => {\n const deployInfo = getDeployTargetLabel();\n\n let services: string[] = [];\n\n do {\n const allServices = getServiceNames(appName);\n const allText = '[ all ]';\n\n const service = await promptSelect('Select services to tear down', [allText, ...allServices]);\n\n if (service === allText) {\n services = allServices;\n break;\n }\n\n services.push(service);\n } while (await promptYesNo('Do you want to tear down another service?'));\n\n services = [...new Set(services)];\n\n console.log(`\\nThis will DESTROY the web, api and inf stacks (including all stored data) for: ${services.join(', ')}`);\n const confirmation = await promptText(`Type '${deployInfo}' to confirm`);\n\n if (confirmation !== deployInfo) {\n console.log('Confirmation did not match. Exiting!');\n return { kind: 'cancelled' };\n }\n\n return { kind: 'services', appName, services };\n};\n"]}
1
+ {"version":3,"file":"deployPrompts.js","sourceRoot":"","sources":["../../../src/lib/deployPrompts.ts"],"names":[],"mappings":"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","sourcesContent":["// The interactive question flow shared by `qpq go` (sequential AWS deploy) and\n// a future dockerized variant: pick an app, confirm the target, pick services\n// (or domain/bootstrap), pick stacks. buildDeployPlanFromArgs is the\n// non-interactive shortcut (`qpq go all all`) that skips every prompt.\nimport { Nullable } from '@qpqjs/core';\n\nimport { getPositionalArgs } from './args';\nimport { getServiceNames } from './discovery';\nimport { getDeployTargetLabel } from './getDeployTargetLabel';\nimport { promptSelect, promptTypedConfirmation, promptYesNo } from './prompts';\nimport { CliError } from './CliError';\nimport { createOutput } from './out';\n\nconst out = createOutput('deploy-prompts');\n\nexport type StackType = 'all' | 'inf' | 'api' | 'web' | 'views';\n\nconst STACK_TYPES: StackType[] = ['all', 'inf', 'api', 'web', 'views'];\n\nexport type DeployPlan =\n | { kind: 'cancelled' }\n // Deploy only the account-level guardrails stack (one per AWS account).\n | { kind: 'account'; appName: string }\n // Deploy the bootstrap stack (and optionally the domain stack first).\n | { kind: 'bootstrap'; appName: string; includeDomain: boolean }\n // Deploy the selected stacks for the selected services.\n | {\n kind: 'services';\n appName: string;\n services: string[];\n stacks: StackType;\n };\n\n// Non-interactive deploy plan from positional args: `qpq go <services> [stacks]`.\n// services: 'all' | comma-separated service names | 'account' | 'domain' | 'bootstrap'\n// stacks: all | inf | api | web | views (defaults to 'all')\n// Returns null when no positional services token is given, so callers fall back\n// to the interactive promptDeployPlan. Passing args is an explicit opt-in to a\n// prompt-free deploy, so the \"Deploying to: [env]\" confirmation is skipped too.\nexport const buildDeployPlanFromArgs = (appName: string, argv: string[]): Nullable<DeployPlan> => {\n const [servicesArg, stacksArg] = getPositionalArgs(argv, ['--app', '--deployment']);\n\n if (!servicesArg) {\n return null;\n }\n\n if (servicesArg === 'account') {\n return { kind: 'account', appName };\n }\n if (servicesArg === 'domain') {\n return { kind: 'bootstrap', appName, includeDomain: true };\n }\n if (servicesArg === 'bootstrap') {\n return { kind: 'bootstrap', appName, includeDomain: false };\n }\n\n const allServices = getServiceNames(appName);\n const services =\n servicesArg === 'all'\n ? allServices\n : servicesArg\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n\n const unknownServices = services.filter((service) => !allServices.includes(service));\n if (unknownServices.length > 0) {\n throw new CliError(`Unknown service(s): ${unknownServices.join(', ')}. Valid services: ${allServices.join(', ')}`);\n }\n\n const stacks = (stacksArg ?? 'all') as StackType;\n if (!STACK_TYPES.includes(stacks)) {\n throw new CliError(`Unknown stacks '${stacksArg}'. Valid stacks: ${STACK_TYPES.join(', ')}`);\n }\n\n return { kind: 'services', appName, services: [...new Set(services)], stacks };\n};\n\nexport const promptDeployPlan = async (appName: string): Promise<DeployPlan> => {\n const deployInfo = getDeployTargetLabel();\n\n if (!(await promptYesNo(`Deploying to: [${deployInfo}]`))) {\n out.log('Exiting!');\n return { kind: 'cancelled' };\n }\n\n let services: string[] = [];\n\n do {\n const allServices = getServiceNames(appName);\n\n // Bracketed so the meta options can't be mistaken for actual services.\n const allText = '[ all ]';\n const accountText = '[ account ]';\n const domainEnvText = '[ domain ]';\n const bootstrapEnvText = '[ bootstrap environment ]';\n\n const service = await promptSelect('Select services to deploy', [allText, ...allServices, accountText, domainEnvText, bootstrapEnvText]);\n\n if (service === allText) {\n services = allServices;\n break;\n }\n if (service === accountText) {\n return { kind: 'account', appName };\n }\n if (service === domainEnvText) {\n return { kind: 'bootstrap', appName, includeDomain: true };\n }\n if (service === bootstrapEnvText) {\n return { kind: 'bootstrap', appName, includeDomain: false };\n }\n\n services.push(service);\n } while (await promptYesNo('Do you want to deploy another service?'));\n\n services = [...new Set(services)];\n\n const stacks = (await promptSelect('Stacks to deploy', ['all', 'inf', 'api', 'web', 'views'])) as StackType;\n\n return { kind: 'services', appName, services, stacks };\n};\n\nexport type TeardownPlan =\n | { kind: 'cancelled' }\n // Destroy the web, api and inf stacks for the selected services.\n | { kind: 'services'; appName: string; services: string[] };\n\n// The question flow for `qpq teardown`: pick services, then type the deploy\n// target back to confirm, destroying inf stacks takes user data (key value\n// stores, user directories) with it, so a y/n prompt isn't enough friction.\nexport const promptTeardownPlan = async (appName: string): Promise<TeardownPlan> => {\n const deployInfo = getDeployTargetLabel();\n\n let services: string[] = [];\n\n do {\n const allServices = getServiceNames(appName);\n const allText = '[ all ]';\n\n const service = await promptSelect('Select services to tear down', [allText, ...allServices]);\n\n if (service === allText) {\n services = allServices;\n break;\n }\n\n services.push(service);\n } while (await promptYesNo('Do you want to tear down another service?'));\n\n services = [...new Set(services)];\n\n out.log(`\\nThis will DESTROY the web, api and inf stacks (including all stored data) for: ${services.join(', ')}`);\n if (!(await promptTypedConfirmation(deployInfo))) {\n return { kind: 'cancelled' };\n }\n\n return { kind: 'services', appName, services };\n};\n"]}
@@ -4,9 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.writeUserScriptChildEntry = exports.writeDevServerEntry = exports.DEV_SERVER_PUBLIC_FILE_STORAGE_PORT_ENV = exports.DEV_SERVER_PUBLIC_HOST_ENV = void 0;
7
+ const core_1 = require("@qpqjs/core");
8
+ const deploy_rspack_1 = require("@qpqjs/deploy-rspack");
7
9
  const fs_1 = __importDefault(require("fs"));
8
10
  const path_1 = __importDefault(require("path"));
9
- const deploy_rspack_1 = require("@qpqjs/deploy-rspack");
10
11
  const devServerPorts_1 = require("./devServerPorts");
11
12
  /** Env vars the docker image sets so the running server knows how the outside world reaches it. */
12
13
  exports.DEV_SERVER_PUBLIC_HOST_ENV = 'QPQ_PUBLIC_HOST';
@@ -49,7 +50,7 @@ const getRunCall = (mode) => {
49
50
  * shares one bundle shape, so a local migration run rehearses exactly the deployed path.
50
51
  */
51
52
  const writeDevServerEntry = (root, appName, mode = 'serve') => {
52
- const entryDir = path_1.default.join(root, 'dist', 'qpq', 'dev-server');
53
+ const entryDir = (0, core_1.getDevServerDistPath)(root);
53
54
  const entryPath = path_1.default.join(entryDir, ENTRY_FILE_NAMES[mode]);
54
55
  const overridesPath = (0, devServerPorts_1.getDevServerConfigPath)(appName);
55
56
  const overridesImport = fs_1.default.existsSync(overridesPath)
@@ -105,11 +106,11 @@ exports.writeDevServerEntry = writeDevServerEntry;
105
106
  * serves that one call with the service's config and module loader, then exits.
106
107
  */
107
108
  const writeUserScriptChildEntry = (root) => {
108
- const entryDir = path_1.default.join(root, 'dist', 'qpq', 'dev-server');
109
+ const entryDir = (0, core_1.getDevServerDistPath)(root);
109
110
  const entryPath = path_1.default.join(entryDir, USER_SCRIPT_CHILD_ENTRY_FILE_NAME);
110
111
  fs_1.default.mkdirSync(entryDir, { recursive: true });
111
112
  fs_1.default.writeFileSync(entryPath, `// GENERATED by qpq — do not edit.
112
- import { qpqCoreUtils } from '@qpqjs/core';
113
+ import { getDevServerDistPath, qpqCoreUtils } from '@qpqjs/core';
113
114
  import { runUserScriptChildMain } from '@qpqjs/actionprocessor-node';
114
115
  import {
115
116
  qpqConfigs,
@@ -1 +1 @@
1
- {"version":3,"file":"devServerEntry.js","sourceRoot":"","sources":["../../../src/lib/devServerEntry.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nimport { USER_SCRIPT_CHILD_BUNDLE_NAME } from '@qpqjs/deploy-rspack';\n\nimport { DEFAULT_DEV_SERVER_PORTS, getDevServerConfigPath } from './devServerPorts';\n\n/**\n * What the generated bootstrap does: serve forever, run pending migrations once and exit, or\n * both in turn (the docker image, where nothing else ever deploys and so nothing else would\n * run the migrations).\n */\nexport type DevServerEntryMode = 'serve' | 'migrate' | 'migrate-then-serve';\n\n/** Env vars the docker image sets so the running server knows how the outside world reaches it. */\nexport const DEV_SERVER_PUBLIC_HOST_ENV = 'QPQ_PUBLIC_HOST';\nexport const DEV_SERVER_PUBLIC_FILE_STORAGE_PORT_ENV = 'QPQ_PUBLIC_FILE_STORAGE_PORT';\n\nconst USER_SCRIPT_CHILD_ENTRY_FILE_NAME = 'user-script-child.entry.ts';\n\nconst ENTRY_FILE_NAMES: Record<DevServerEntryMode, string> = {\n serve: 'entry.ts',\n migrate: 'migrate.entry.ts',\n 'migrate-then-serve': 'entry.ts',\n};\n\nconst reportMigrations = `(ran) => {\n const total = Object.values(ran).reduce((sum, keys) => sum + keys.length, 0);\n for (const [service, keys] of Object.entries(ran)) {\n for (const key of keys) {\n console.log(\\` [\\${service}] \\${key}\\`);\n }\n }\n console.log(total === 0 ? 'No pending migrations.' : \\`Ran \\${total} migration(s).\\`);\n }`;\n\nconst getRunCall = (mode: DevServerEntryMode): string => {\n if (mode === 'serve') {\n return 'startDevServer(devServerConfig, devServerConfigOverrides);';\n }\n\n const afterMigrations = mode === 'migrate' ? 'process.exit(0);' : 'return startDevServer(devServerConfig, devServerConfigOverrides);';\n\n return `runMigrations(devServerConfig, devServerConfigOverrides)\n .then(${reportMigrations})\n .then(() => {\n ${afterMigrations}\n })\n .catch((error) => {\n console.error('Migration failed:', error);\n // Non-zero so CI, shell chains and a container restart policy actually notice.\n process.exit(1);\n });`;\n};\n\n/**\n * Writes the dev-server bootstrap for an app and returns its path. Generated per build so it\n * can import the optional app-owned overrides file (apps/<app>/devServer.config.ts) and the\n * `quidproquo-dynamic-loader` virtual module QpqPlugin injects at bundle time. Every mode\n * shares one bundle shape, so a local migration run rehearses exactly the deployed path.\n */\nexport const writeDevServerEntry = (root: string, appName: string, mode: DevServerEntryMode = 'serve'): string => {\n const entryDir = path.join(root, 'dist', 'qpq', 'dev-server');\n const entryPath = path.join(entryDir, ENTRY_FILE_NAMES[mode]);\n\n const overridesPath = getDevServerConfigPath(appName);\n const overridesImport = fs.existsSync(overridesPath)\n ? `import devServerConfigOverrides from ${JSON.stringify(overridesPath.replace(/\\.ts$/, ''))};`\n : 'const devServerConfigOverrides = {};';\n\n const imports = mode === 'serve' ? 'startDevServer' : mode === 'migrate' ? 'runMigrations' : 'runMigrations, startDevServer';\n\n fs.mkdirSync(entryDir, { recursive: true });\n fs.writeFileSync(\n entryPath,\n `// GENERATED by qpq — do not edit.\nimport { ${imports} } from '@qpqjs/dev-server';\nimport path from 'path';\nimport {\n qpqConfigs,\n qpqDynamicModuleLoaderForService,\n // @ts-ignore - Special build-time virtual module\n} from 'quidproquo-dynamic-loader';\n\n${overridesImport}\n\n// Ports are defaults here; the app's devServer.config.ts \\`ports\\` win when the server resolves its config.\nconst devServerConfig = {\n serverDomain: 'localhost',\n serverPort: ${DEFAULT_DEV_SERVER_PORTS.api},\n webSocketPort: ${DEFAULT_DEV_SERVER_PORTS.webSocket},\n // Scoped per app so running the dev server for two apps out of the same\n // repo root doesn't share kvs/file-storage/config state.\n runtimePath: ${JSON.stringify(path.join('.qpq-runtime', appName))},\n fileStorageConfig: {\n // Secure file urls are absolute, so a container has to be told the address browsers use.\n secureUrlHost: process.env.${DEV_SERVER_PUBLIC_HOST_ENV} || 'localhost',\n secureUrlPort: ${DEFAULT_DEV_SERVER_PORTS.fileStorage},\n secureUrlPublicPort: Number(process.env.${DEV_SERVER_PUBLIC_FILE_STORAGE_PORT_ENV}) || undefined,\n // No fixed secret: the server mints a random one per process, so a secure url token\n // (which names a filesystem path) cannot be forged by anyone who has read this file.\n },\n qpqConfigs,\n dynamicModuleLoader: qpqDynamicModuleLoaderForService,\n // The user-script child bundle is built beside this one (see writeUserScriptChildEntry).\n userScriptChildModulePath: path.join(path.dirname(process.argv[1]), ${JSON.stringify(`${USER_SCRIPT_CHILD_BUNDLE_NAME}.js`)}),\n // QPQ_WEB_ROOT (set in the docker platform image) turns on static web serving.\n webRoot: process.env.QPQ_WEB_ROOT,\n logServiceName: 'admin',\n delay: { default: 0 },\n};\n\n${getRunCall(mode)}\n`,\n );\n\n return entryPath;\n};\n\n/**\n * Writes the dev server's user-script child entry and returns its path. The server forks the\n * bundle this becomes once per script call, with the service name as its argument; the child\n * serves that one call with the service's config and module loader, then exits.\n */\nexport const writeUserScriptChildEntry = (root: string): string => {\n const entryDir = path.join(root, 'dist', 'qpq', 'dev-server');\n const entryPath = path.join(entryDir, USER_SCRIPT_CHILD_ENTRY_FILE_NAME);\n\n fs.mkdirSync(entryDir, { recursive: true });\n fs.writeFileSync(\n entryPath,\n `// GENERATED by qpq — do not edit.\nimport { qpqCoreUtils } from '@qpqjs/core';\nimport { runUserScriptChildMain } from '@qpqjs/actionprocessor-node';\nimport {\n qpqConfigs,\n qpqDynamicModuleLoaderForService,\n // @ts-ignore - Special build-time virtual module\n} from 'quidproquo-dynamic-loader';\n\nexport const main = (): void => {\n const serviceName = process.argv[2];\n const qpqConfig = qpqConfigs.find((config) => qpqCoreUtils.getApplicationModuleName(config) === serviceName);\n if (!qpqConfig) {\n throw new Error(\\`User script child: no service named [\\${serviceName}]\\`);\n }\n\n runUserScriptChildMain(qpqConfig, (runtime) => qpqDynamicModuleLoaderForService(serviceName, runtime));\n};\n\nmain();\n`,\n );\n\n return entryPath;\n};\n"]}
1
+ {"version":3,"file":"devServerEntry.js","sourceRoot":"","sources":["../../../src/lib/devServerEntry.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDevServerDistPath } from '@qpqjs/core';\nimport { USER_SCRIPT_CHILD_BUNDLE_NAME } from '@qpqjs/deploy-rspack';\n\nimport fs from 'fs';\nimport path from 'path';\n\nimport { DEFAULT_DEV_SERVER_PORTS, getDevServerConfigPath } from './devServerPorts';\n\n/**\n * What the generated bootstrap does: serve forever, run pending migrations once and exit, or\n * both in turn (the docker image, where nothing else ever deploys and so nothing else would\n * run the migrations).\n */\nexport type DevServerEntryMode = 'serve' | 'migrate' | 'migrate-then-serve';\n\n/** Env vars the docker image sets so the running server knows how the outside world reaches it. */\nexport const DEV_SERVER_PUBLIC_HOST_ENV = 'QPQ_PUBLIC_HOST';\nexport const DEV_SERVER_PUBLIC_FILE_STORAGE_PORT_ENV = 'QPQ_PUBLIC_FILE_STORAGE_PORT';\n\nconst USER_SCRIPT_CHILD_ENTRY_FILE_NAME = 'user-script-child.entry.ts';\n\nconst ENTRY_FILE_NAMES: Record<DevServerEntryMode, string> = {\n serve: 'entry.ts',\n migrate: 'migrate.entry.ts',\n 'migrate-then-serve': 'entry.ts',\n};\n\nconst reportMigrations = `(ran) => {\n const total = Object.values(ran).reduce((sum, keys) => sum + keys.length, 0);\n for (const [service, keys] of Object.entries(ran)) {\n for (const key of keys) {\n console.log(\\` [\\${service}] \\${key}\\`);\n }\n }\n console.log(total === 0 ? 'No pending migrations.' : \\`Ran \\${total} migration(s).\\`);\n }`;\n\nconst getRunCall = (mode: DevServerEntryMode): string => {\n if (mode === 'serve') {\n return 'startDevServer(devServerConfig, devServerConfigOverrides);';\n }\n\n const afterMigrations = mode === 'migrate' ? 'process.exit(0);' : 'return startDevServer(devServerConfig, devServerConfigOverrides);';\n\n return `runMigrations(devServerConfig, devServerConfigOverrides)\n .then(${reportMigrations})\n .then(() => {\n ${afterMigrations}\n })\n .catch((error) => {\n console.error('Migration failed:', error);\n // Non-zero so CI, shell chains and a container restart policy actually notice.\n process.exit(1);\n });`;\n};\n\n/**\n * Writes the dev-server bootstrap for an app and returns its path. Generated per build so it\n * can import the optional app-owned overrides file (apps/<app>/devServer.config.ts) and the\n * `quidproquo-dynamic-loader` virtual module QpqPlugin injects at bundle time. Every mode\n * shares one bundle shape, so a local migration run rehearses exactly the deployed path.\n */\nexport const writeDevServerEntry = (root: string, appName: string, mode: DevServerEntryMode = 'serve'): string => {\n const entryDir = getDevServerDistPath(root);\n const entryPath = path.join(entryDir, ENTRY_FILE_NAMES[mode]);\n\n const overridesPath = getDevServerConfigPath(appName);\n const overridesImport = fs.existsSync(overridesPath)\n ? `import devServerConfigOverrides from ${JSON.stringify(overridesPath.replace(/\\.ts$/, ''))};`\n : 'const devServerConfigOverrides = {};';\n\n const imports = mode === 'serve' ? 'startDevServer' : mode === 'migrate' ? 'runMigrations' : 'runMigrations, startDevServer';\n\n fs.mkdirSync(entryDir, { recursive: true });\n fs.writeFileSync(\n entryPath,\n `// GENERATED by qpq — do not edit.\nimport { ${imports} } from '@qpqjs/dev-server';\nimport path from 'path';\nimport {\n qpqConfigs,\n qpqDynamicModuleLoaderForService,\n // @ts-ignore - Special build-time virtual module\n} from 'quidproquo-dynamic-loader';\n\n${overridesImport}\n\n// Ports are defaults here; the app's devServer.config.ts \\`ports\\` win when the server resolves its config.\nconst devServerConfig = {\n serverDomain: 'localhost',\n serverPort: ${DEFAULT_DEV_SERVER_PORTS.api},\n webSocketPort: ${DEFAULT_DEV_SERVER_PORTS.webSocket},\n // Scoped per app so running the dev server for two apps out of the same\n // repo root doesn't share kvs/file-storage/config state.\n runtimePath: ${JSON.stringify(path.join('.qpq-runtime', appName))},\n fileStorageConfig: {\n // Secure file urls are absolute, so a container has to be told the address browsers use.\n secureUrlHost: process.env.${DEV_SERVER_PUBLIC_HOST_ENV} || 'localhost',\n secureUrlPort: ${DEFAULT_DEV_SERVER_PORTS.fileStorage},\n secureUrlPublicPort: Number(process.env.${DEV_SERVER_PUBLIC_FILE_STORAGE_PORT_ENV}) || undefined,\n // No fixed secret: the server mints a random one per process, so a secure url token\n // (which names a filesystem path) cannot be forged by anyone who has read this file.\n },\n qpqConfigs,\n dynamicModuleLoader: qpqDynamicModuleLoaderForService,\n // The user-script child bundle is built beside this one (see writeUserScriptChildEntry).\n userScriptChildModulePath: path.join(path.dirname(process.argv[1]), ${JSON.stringify(`${USER_SCRIPT_CHILD_BUNDLE_NAME}.js`)}),\n // QPQ_WEB_ROOT (set in the docker platform image) turns on static web serving.\n webRoot: process.env.QPQ_WEB_ROOT,\n logServiceName: 'admin',\n delay: { default: 0 },\n};\n\n${getRunCall(mode)}\n`,\n );\n\n return entryPath;\n};\n\n/**\n * Writes the dev server's user-script child entry and returns its path. The server forks the\n * bundle this becomes once per script call, with the service name as its argument; the child\n * serves that one call with the service's config and module loader, then exits.\n */\nexport const writeUserScriptChildEntry = (root: string): string => {\n const entryDir = getDevServerDistPath(root);\n const entryPath = path.join(entryDir, USER_SCRIPT_CHILD_ENTRY_FILE_NAME);\n\n fs.mkdirSync(entryDir, { recursive: true });\n fs.writeFileSync(\n entryPath,\n `// GENERATED by qpq — do not edit.\nimport { getDevServerDistPath, qpqCoreUtils } from '@qpqjs/core';\nimport { runUserScriptChildMain } from '@qpqjs/actionprocessor-node';\nimport {\n qpqConfigs,\n qpqDynamicModuleLoaderForService,\n // @ts-ignore - Special build-time virtual module\n} from 'quidproquo-dynamic-loader';\n\nexport const main = (): void => {\n const serviceName = process.argv[2];\n const qpqConfig = qpqConfigs.find((config) => qpqCoreUtils.getApplicationModuleName(config) === serviceName);\n if (!qpqConfig) {\n throw new Error(\\`User script child: no service named [\\${serviceName}]\\`);\n }\n\n runUserScriptChildMain(qpqConfig, (runtime) => qpqDynamicModuleLoaderForService(serviceName, runtime));\n};\n\nmain();\n`,\n );\n\n return entryPath;\n};\n"]}
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.readDevServerPorts = exports.getDevServerConfigPath = exports.DEFAULT_DEV_SERVER_PORTS = void 0;
7
+ const deploy_aws_1 = require("@qpqjs/deploy-aws");
7
8
  const fs_1 = __importDefault(require("fs"));
8
9
  const path_1 = __importDefault(require("path"));
9
10
  const discovery_1 = require("./discovery");
@@ -22,14 +23,11 @@ exports.getDevServerConfigPath = getDevServerConfigPath;
22
23
  * Requiring the TS module relies on the caller running with TS require hooks, as every command does.
23
24
  */
24
25
  const readDevServerPorts = (appName) => {
25
- var _a;
26
26
  const configPath = (0, exports.getDevServerConfigPath)(appName);
27
27
  if (!fs_1.default.existsSync(configPath)) {
28
28
  return exports.DEFAULT_DEV_SERVER_PORTS;
29
29
  }
30
- // eslint-disable-next-line @typescript-eslint/no-require-imports
31
- const loaded = require(configPath.replace(/\.ts$/, ''));
32
- const overrides = ((_a = loaded.default) !== null && _a !== void 0 ? _a : loaded);
30
+ const overrides = (0, deploy_aws_1.requireModuleDefault)(configPath.replace(/\.ts$/, ''));
33
31
  return Object.assign(Object.assign({}, exports.DEFAULT_DEV_SERVER_PORTS), overrides.ports);
34
32
  };
35
33
  exports.readDevServerPorts = readDevServerPorts;
@@ -1 +1 @@
1
- {"version":3,"file":"devServerPorts.js","sourceRoot":"","sources":["../../../src/lib/devServerPorts.ts"],"names":[],"mappings":";;;;;;AAEA,4CAAoB;AACpB,gDAAwB;AAExB,2CAA8C;AAK9C,wHAAwH;AAC3G,QAAA,wBAAwB,GAA2B;IAC9D,GAAG,EAAE,IAAI;IACT,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,IAAI;CAClB,CAAC;AAEF,gGAAgG;AACzF,MAAM,sBAAsB,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,cAAI,CAAC,IAAI,CAAC,IAAA,2BAAe,EAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC;AAAjH,QAAA,sBAAsB,0BAA2F;AAE9H;;;;GAIG;AACI,MAAM,kBAAkB,GAAG,CAAC,OAAe,EAA0B,EAAE;;IAC5E,MAAM,UAAU,GAAG,IAAA,8BAAsB,EAAC,OAAO,CAAC,CAAC;IACnD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,gCAAwB,CAAC;IAClC,CAAC;IAED,iEAAiE;IACjE,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,CAAC,MAAA,MAAM,CAAC,OAAO,mCAAI,MAAM,CAA6B,CAAC;IAEzE,uCAAY,gCAAwB,GAAK,SAAS,CAAC,KAAK,EAAG;AAC7D,CAAC,CAAC;AAXW,QAAA,kBAAkB,sBAW7B","sourcesContent":["import { DevServerConfigOverrides, DevServerPorts } from '@qpqjs/dev-server';\n\nimport fs from 'fs';\nimport path from 'path';\n\nimport { getAppDirectory } from './discovery';\n\n/** Every dev server listener, with the app's overrides applied. */\nexport type ResolvedDevServerPorts = Required<DevServerPorts>;\n\n/** The ports a dev server listens on when the app sets none: api + static web, websockets, file storage secure urls. */\nexport const DEFAULT_DEV_SERVER_PORTS: ResolvedDevServerPorts = {\n api: 8080,\n webSocket: 8888,\n fileStorage: 3001,\n};\n\n/** Path of the app's optional dev server settings module (`apps/<app>/devServer.config.ts`). */\nexport const getDevServerConfigPath = (appName: string): string => path.join(getAppDirectory(appName), 'devServer.config.ts');\n\n/**\n * The app's dev server ports: `ports` from apps/<app>/devServer.config.ts over the defaults. The\n * same numbers the running server resolves, so builds that bake or expose ports agree with it.\n * Requiring the TS module relies on the caller running with TS require hooks, as every command does.\n */\nexport const readDevServerPorts = (appName: string): ResolvedDevServerPorts => {\n const configPath = getDevServerConfigPath(appName);\n if (!fs.existsSync(configPath)) {\n return DEFAULT_DEV_SERVER_PORTS;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const loaded = require(configPath.replace(/\\.ts$/, ''));\n const overrides = (loaded.default ?? loaded) as DevServerConfigOverrides;\n\n return { ...DEFAULT_DEV_SERVER_PORTS, ...overrides.ports };\n};\n"]}
1
+ {"version":3,"file":"devServerPorts.js","sourceRoot":"","sources":["../../../src/lib/devServerPorts.ts"],"names":[],"mappings":";;;;;;AAAA,kDAAyD;AAGzD,4CAAoB;AACpB,gDAAwB;AAExB,2CAA8C;AAK9C,wHAAwH;AAC3G,QAAA,wBAAwB,GAA2B;IAC9D,GAAG,EAAE,IAAI;IACT,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,IAAI;CAClB,CAAC;AAEF,gGAAgG;AACzF,MAAM,sBAAsB,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,cAAI,CAAC,IAAI,CAAC,IAAA,2BAAe,EAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC;AAAjH,QAAA,sBAAsB,0BAA2F;AAE9H;;;;GAIG;AACI,MAAM,kBAAkB,GAAG,CAAC,OAAe,EAA0B,EAAE;IAC5E,MAAM,UAAU,GAAG,IAAA,8BAAsB,EAAC,OAAO,CAAC,CAAC;IACnD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,gCAAwB,CAAC;IAClC,CAAC;IAED,MAAM,SAAS,GAAG,IAAA,iCAAoB,EAA2B,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAElG,uCAAY,gCAAwB,GAAK,SAAS,CAAC,KAAK,EAAG;AAC7D,CAAC,CAAC;AATW,QAAA,kBAAkB,sBAS7B","sourcesContent":["import { requireModuleDefault } from '@qpqjs/deploy-aws';\nimport { DevServerConfigOverrides, DevServerPorts } from '@qpqjs/dev-server';\n\nimport fs from 'fs';\nimport path from 'path';\n\nimport { getAppDirectory } from './discovery';\n\n/** Every dev server listener, with the app's overrides applied. */\nexport type ResolvedDevServerPorts = Required<DevServerPorts>;\n\n/** The ports a dev server listens on when the app sets none: api + static web, websockets, file storage secure urls. */\nexport const DEFAULT_DEV_SERVER_PORTS: ResolvedDevServerPorts = {\n api: 8080,\n webSocket: 8888,\n fileStorage: 3001,\n};\n\n/** Path of the app's optional dev server settings module (`apps/<app>/devServer.config.ts`). */\nexport const getDevServerConfigPath = (appName: string): string => path.join(getAppDirectory(appName), 'devServer.config.ts');\n\n/**\n * The app's dev server ports: `ports` from apps/<app>/devServer.config.ts over the defaults. The\n * same numbers the running server resolves, so builds that bake or expose ports agree with it.\n * Requiring the TS module relies on the caller running with TS require hooks, as every command does.\n */\nexport const readDevServerPorts = (appName: string): ResolvedDevServerPorts => {\n const configPath = getDevServerConfigPath(appName);\n if (!fs.existsSync(configPath)) {\n return DEFAULT_DEV_SERVER_PORTS;\n }\n\n const overrides = requireModuleDefault<DevServerConfigOverrides>(configPath.replace(/\\.ts$/, ''));\n\n return { ...DEFAULT_DEV_SERVER_PORTS, ...overrides.ports };\n};\n"]}
@@ -3,8 +3,6 @@ export declare const getAppsDirectory: () => string;
3
3
  export declare const getAppDirectory: (appName: string) => string;
4
4
  export declare const getServiceDirectory: (appName: string, service: string) => string;
5
5
  export declare const getAvailableApps: () => string[];
6
- export declare const getAllServiceNames: (appName: string) => string[];
7
- export declare const serviceHasSubdir: (appName: string, service: string, subdir: string) => boolean;
8
6
  export declare const getServiceNamesWithSubdir: (appName: string, subdir: string) => string[];
9
7
  export declare const getServiceNames: (appName: string) => string[];
10
8
  export declare const getServiceNamesWithViews: (appName: string) => string[];
@@ -1,56 +1,23 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getServiceNamesWithViews = exports.getServiceNames = exports.getServiceNamesWithSubdir = exports.serviceHasSubdir = exports.getAllServiceNames = exports.getAvailableApps = exports.getServiceDirectory = exports.getAppDirectory = exports.getAppsDirectory = exports.getRoot = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const path_1 = __importDefault(require("path"));
9
- // The consumer repo root — qpq always runs from the workspace root (npm
10
- // scripts set cwd there).
3
+ exports.getServiceNamesWithViews = exports.getServiceNames = exports.getServiceNamesWithSubdir = exports.getAvailableApps = exports.getServiceDirectory = exports.getAppDirectory = exports.getAppsDirectory = exports.getRoot = void 0;
4
+ const deploy_rspack_1 = require("@qpqjs/deploy-rspack");
5
+ // The consumer repo root, qpq always runs from the workspace root (npm
6
+ // scripts set cwd there). Discovery itself is deploy-rspack's; these bind it to that root.
11
7
  const getRoot = () => process.cwd();
12
8
  exports.getRoot = getRoot;
13
- const getAppsDirectory = () => path_1.default.join((0, exports.getRoot)(), 'apps');
9
+ const getAppsDirectory = () => (0, deploy_rspack_1.getAppsPath)((0, exports.getRoot)());
14
10
  exports.getAppsDirectory = getAppsDirectory;
15
- const getAppDirectory = (appName) => path_1.default.join((0, exports.getAppsDirectory)(), appName);
11
+ const getAppDirectory = (appName) => (0, deploy_rspack_1.getAppPath)((0, exports.getRoot)(), appName);
16
12
  exports.getAppDirectory = getAppDirectory;
17
- const getServiceDirectory = (appName, service) => path_1.default.join((0, exports.getAppDirectory)(appName), 'services', service);
13
+ const getServiceDirectory = (appName, service) => (0, deploy_rspack_1.getServicePath)((0, exports.getRoot)(), appName, service);
18
14
  exports.getServiceDirectory = getServiceDirectory;
19
- // Apps are the directories under apps/ that contain a services/ directory.
20
- const getAvailableApps = () => {
21
- const appsPath = (0, exports.getAppsDirectory)();
22
- if (!fs_1.default.existsSync(appsPath)) {
23
- return [];
24
- }
25
- return fs_1.default.readdirSync(appsPath).filter((name) => {
26
- const servicesPath = path_1.default.join((0, exports.getAppDirectory)(name), 'services');
27
- return fs_1.default.statSync((0, exports.getAppDirectory)(name)).isDirectory() && fs_1.default.existsSync(servicesPath) && fs_1.default.statSync(servicesPath).isDirectory();
28
- });
29
- };
15
+ const getAvailableApps = () => (0, deploy_rspack_1.getAvailableAppNames)((0, exports.getRoot)());
30
16
  exports.getAvailableApps = getAvailableApps;
31
- // Every service directory under an app's services/ folder (no subdir filter).
32
- const getAllServiceNames = (appName) => {
33
- const directoryPath = path_1.default.join((0, exports.getAppDirectory)(appName), 'services');
34
- if (!fs_1.default.existsSync(directoryPath)) {
35
- return [];
36
- }
37
- return fs_1.default.readdirSync(directoryPath).filter((name) => fs_1.default.statSync((0, exports.getServiceDirectory)(appName, name)).isDirectory());
38
- };
39
- exports.getAllServiceNames = getAllServiceNames;
40
- // True when a service contains the given child directory (e.g. 'service' for a
41
- // backend, 'views' for a frontend).
42
- const serviceHasSubdir = (appName, service, subdir) => {
43
- const subdirPath = path_1.default.join((0, exports.getServiceDirectory)(appName, service), subdir);
44
- return fs_1.default.existsSync(subdirPath) && fs_1.default.statSync(subdirPath).isDirectory();
45
- };
46
- exports.serviceHasSubdir = serviceHasSubdir;
47
- // Services that contain a given child directory.
48
- const getServiceNamesWithSubdir = (appName, subdir) => (0, exports.getAllServiceNames)(appName).filter((service) => (0, exports.serviceHasSubdir)(appName, service, subdir));
17
+ const getServiceNamesWithSubdir = (appName, subdir) => (0, deploy_rspack_1.getServiceNamesWithSubdir)((0, exports.getRoot)(), appName, subdir);
49
18
  exports.getServiceNamesWithSubdir = getServiceNamesWithSubdir;
50
- // Backend services — those with a service/ subdir.
51
- const getServiceNames = (appName) => (0, exports.getServiceNamesWithSubdir)(appName, 'service');
19
+ const getServiceNames = (appName) => (0, deploy_rspack_1.getBackendServiceNames)((0, exports.getRoot)(), appName);
52
20
  exports.getServiceNames = getServiceNames;
53
- // Backend services that also ship a frontend (views/ subdir).
54
- const getServiceNamesWithViews = (appName) => (0, exports.getServiceNames)(appName).filter((service) => (0, exports.serviceHasSubdir)(appName, service, 'views'));
21
+ const getServiceNamesWithViews = (appName) => (0, deploy_rspack_1.getServiceNamesWithViews)((0, exports.getRoot)(), appName);
55
22
  exports.getServiceNamesWithViews = getServiceNamesWithViews;
56
23
  //# sourceMappingURL=discovery.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../../src/lib/discovery.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\n// The consumer repo root — qpq always runs from the workspace root (npm\n// scripts set cwd there).\nexport const getRoot = (): string => process.cwd();\n\nexport const getAppsDirectory = (): string => path.join(getRoot(), 'apps');\nexport const getAppDirectory = (appName: string): string => path.join(getAppsDirectory(), appName);\nexport const getServiceDirectory = (appName: string, service: string): string => path.join(getAppDirectory(appName), 'services', service);\n\n// Apps are the directories under apps/ that contain a services/ directory.\nexport const getAvailableApps = (): string[] => {\n const appsPath = getAppsDirectory();\n\n if (!fs.existsSync(appsPath)) {\n return [];\n }\n\n return fs.readdirSync(appsPath).filter((name) => {\n const servicesPath = path.join(getAppDirectory(name), 'services');\n return fs.statSync(getAppDirectory(name)).isDirectory() && fs.existsSync(servicesPath) && fs.statSync(servicesPath).isDirectory();\n });\n};\n\n// Every service directory under an app's services/ folder (no subdir filter).\nexport const getAllServiceNames = (appName: string): string[] => {\n const directoryPath = path.join(getAppDirectory(appName), 'services');\n\n if (!fs.existsSync(directoryPath)) {\n return [];\n }\n\n return fs.readdirSync(directoryPath).filter((name) => fs.statSync(getServiceDirectory(appName, name)).isDirectory());\n};\n\n// True when a service contains the given child directory (e.g. 'service' for a\n// backend, 'views' for a frontend).\nexport const serviceHasSubdir = (appName: string, service: string, subdir: string): boolean => {\n const subdirPath = path.join(getServiceDirectory(appName, service), subdir);\n return fs.existsSync(subdirPath) && fs.statSync(subdirPath).isDirectory();\n};\n\n// Services that contain a given child directory.\nexport const getServiceNamesWithSubdir = (appName: string, subdir: string): string[] =>\n getAllServiceNames(appName).filter((service) => serviceHasSubdir(appName, service, subdir));\n\n// Backend services — those with a service/ subdir.\nexport const getServiceNames = (appName: string): string[] => getServiceNamesWithSubdir(appName, 'service');\n\n// Backend services that also ship a frontend (views/ subdir).\nexport const getServiceNamesWithViews = (appName: string): string[] =>\n getServiceNames(appName).filter((service) => serviceHasSubdir(appName, service, 'views'));\n"]}
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../../src/lib/discovery.ts"],"names":[],"mappings":";;;AAAA,wDAQ8B;AAE9B,uEAAuE;AACvE,2FAA2F;AACpF,MAAM,OAAO,GAAG,GAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;AAAtC,QAAA,OAAO,WAA+B;AAE5C,MAAM,gBAAgB,GAAG,GAAW,EAAE,CAAC,IAAA,2BAAW,EAAC,IAAA,eAAO,GAAE,CAAC,CAAC;AAAxD,QAAA,gBAAgB,oBAAwC;AAC9D,MAAM,eAAe,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,IAAA,0BAAU,EAAC,IAAA,eAAO,GAAE,EAAE,OAAO,CAAC,CAAC;AAA9E,QAAA,eAAe,mBAA+D;AACpF,MAAM,mBAAmB,GAAG,CAAC,OAAe,EAAE,OAAe,EAAU,EAAE,CAAC,IAAA,8BAAc,EAAC,IAAA,eAAO,GAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAAhH,QAAA,mBAAmB,uBAA6F;AAEtH,MAAM,gBAAgB,GAAG,GAAa,EAAE,CAAC,IAAA,oCAAoB,EAAC,IAAA,eAAO,GAAE,CAAC,CAAC;AAAnE,QAAA,gBAAgB,oBAAmD;AAEzE,MAAM,yBAAyB,GAAG,CAAC,OAAe,EAAE,MAAc,EAAY,EAAE,CAAC,IAAA,yCAA2B,EAAC,IAAA,eAAO,GAAE,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAAnI,QAAA,yBAAyB,6BAA0G;AAEzI,MAAM,eAAe,GAAG,CAAC,OAAe,EAAY,EAAE,CAAC,IAAA,sCAAsB,EAAC,IAAA,eAAO,GAAE,EAAE,OAAO,CAAC,CAAC;AAA5F,QAAA,eAAe,mBAA6E;AAElG,MAAM,wBAAwB,GAAG,CAAC,OAAe,EAAY,EAAE,CAAC,IAAA,wCAA0B,EAAC,IAAA,eAAO,GAAE,EAAE,OAAO,CAAC,CAAC;AAAzG,QAAA,wBAAwB,4BAAiF","sourcesContent":["import {\n getAppPath,\n getAppsPath,\n getAvailableAppNames,\n getBackendServiceNames,\n getServiceNamesWithSubdir as getServiceNamesWithSubdirIn,\n getServiceNamesWithViews as getServiceNamesWithViewsIn,\n getServicePath,\n} from '@qpqjs/deploy-rspack';\n\n// The consumer repo root, qpq always runs from the workspace root (npm\n// scripts set cwd there). Discovery itself is deploy-rspack's; these bind it to that root.\nexport const getRoot = (): string => process.cwd();\n\nexport const getAppsDirectory = (): string => getAppsPath(getRoot());\nexport const getAppDirectory = (appName: string): string => getAppPath(getRoot(), appName);\nexport const getServiceDirectory = (appName: string, service: string): string => getServicePath(getRoot(), appName, service);\n\nexport const getAvailableApps = (): string[] => getAvailableAppNames(getRoot());\n\nexport const getServiceNamesWithSubdir = (appName: string, subdir: string): string[] => getServiceNamesWithSubdirIn(getRoot(), appName, subdir);\n\nexport const getServiceNames = (appName: string): string[] => getBackendServiceNames(getRoot(), appName);\n\nexport const getServiceNamesWithViews = (appName: string): string[] => getServiceNamesWithViewsIn(getRoot(), appName);\n"]}
@@ -18,6 +18,8 @@ const path_1 = __importDefault(require("path"));
18
18
  const discovery_1 = require("./discovery");
19
19
  const runCommand_1 = require("./runCommand");
20
20
  const runTasks_1 = require("./runTasks");
21
+ const out_1 = require("./out");
22
+ const out = (0, out_1.createOutput)('hooks');
21
23
  // App lifecycle hooks: apps/<app>/package.json scripts named `qpq:<hook>`
22
24
  // (e.g. qpq:postinstall, qpq:prebuild). The CLI invokes them when present;
23
25
  // apps without the hook are skipped silently.
@@ -32,7 +34,7 @@ const runAppHook = (appName, hook) => __awaiter(void 0, void 0, void 0, function
32
34
  if (!((_a = packageJson.scripts) === null || _a === void 0 ? void 0 : _a[scriptName])) {
33
35
  return false;
34
36
  }
35
- console.log(`Running ${scriptName} for [${appName}]`);
37
+ out.log(`Running ${scriptName} for [${appName}]`);
36
38
  yield (0, runCommand_1.runCommand)('npm', ['run', scriptName], { cwd: (0, discovery_1.getAppDirectory)(appName) });
37
39
  return true;
38
40
  });
@@ -148,7 +150,7 @@ const runHookForPackages = (candidates, hook, jobs) => __awaiter(void 0, void 0,
148
150
  .map((candidate) => (Object.assign(Object.assign({}, candidate), { scriptName: resolveHookScript(candidate.pkg, hook) })))
149
151
  .filter((candidate) => candidate.scriptName !== undefined);
150
152
  if (hookPackages.length === 0) {
151
- console.log(`No app or workspace package defines a "qpq:${hook}" or "${hook}" script, nothing to run.`);
153
+ out.log(`No app or workspace package defines a "qpq:${hook}" or "${hook}" script, nothing to run.`);
152
154
  return [];
153
155
  }
154
156
  // Dependency edges only count between packages that are part of this run:
@@ -163,7 +165,7 @@ const runHookForPackages = (candidates, hook, jobs) => __awaiter(void 0, void 0,
163
165
  run: () => (0, runCommand_1.runCommandPrefixed)(hookPackage.label, 'npm', ['run', hookPackage.scriptName], { cwd: hookPackage.dir, color: (0, runCommand_1.nextPrefixColor)() }),
164
166
  });
165
167
  });
166
- console.log(`Running ${hook} hooks in ${tasks.length} package(s) (${jobs} jobs, dependency ordered)`);
168
+ out.log(`Running ${hook} hooks in ${tasks.length} package(s) (${jobs} jobs, dependency ordered)`);
167
169
  return (0, runTasks_1.runDependencyTasks)(tasks, jobs);
168
170
  });
169
171
  /** Runs a hook in every app and workspace package that defines it. Returns the failures; callers decide fatality. */
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../src/lib/hooks.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nimport { getAppDirectory, getAppsDirectory, getAvailableApps, getRoot } from './discovery';\nimport { nextPrefixColor, runCommand, runCommandPrefixed } from './runCommand';\nimport { runDependencyTasks, TaskFailure } from './runTasks';\n\ninterface HookPackageJson {\n name?: string;\n scripts?: Record<string, string>;\n workspaces?: string[];\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n}\n\n// A directory whose package.json can carry qpq:<hook> scripts: either an app\n// (apps/<app>/package.json, which need not be an npm workspace) or any package\n// matched by the consumer root's `workspaces` globs.\ninterface HookPackage {\n dir: string;\n label: string; // app name or package name, for display\n pkg: HookPackageJson;\n}\n\n// App lifecycle hooks: apps/<app>/package.json scripts named `qpq:<hook>`\n// (e.g. qpq:postinstall, qpq:prebuild). The CLI invokes them when present;\n// apps without the hook are skipped silently.\nexport const runAppHook = async (appName: string, hook: string): Promise<boolean> => {\n const packageJsonPath = path.join(getAppDirectory(appName), 'package.json');\n if (!fs.existsSync(packageJsonPath)) {\n return false;\n }\n\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));\n const scriptName = `qpq:${hook}`;\n if (!packageJson.scripts?.[scriptName]) {\n return false;\n }\n\n console.log(`Running ${scriptName} for [${appName}]`);\n await runCommand('npm', ['run', scriptName], { cwd: getAppDirectory(appName) });\n return true;\n};\n\nconst isDirectory = (candidate: string): boolean => fs.existsSync(candidate) && fs.statSync(candidate).isDirectory();\n\nconst listSubdirectories = (dir: string): string[] => {\n if (!isDirectory(dir)) {\n return [];\n }\n return fs\n .readdirSync(dir)\n .map((name) => path.join(dir, name))\n .filter(isDirectory);\n};\n\n// Expand one `workspaces` glob. Only literal segments and bare `*` are\n// supported, which covers the patterns qpq repos use (e.g.\n// \"apps/*/services/*/models\"); anything fancier (\"**\", braces) matches nothing.\nconst expandWorkspacePattern = (root: string, pattern: string): string[] => {\n let dirs = [root];\n for (const segment of pattern.split('/')) {\n if (segment === '*') {\n dirs = dirs.flatMap(listSubdirectories);\n } else {\n dirs = dirs.map((dir) => path.join(dir, segment)).filter(isDirectory);\n }\n }\n return dirs;\n};\n\nconst readPackageJson = (dir: string): HookPackageJson | undefined => {\n const packageJsonPath = path.join(dir, 'package.json');\n if (!fs.existsSync(packageJsonPath)) {\n return undefined;\n }\n return JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')) as HookPackageJson;\n};\n\n// Every directory that can carry hooks: app dirs plus all workspace packages,\n// deduped (an app dir could itself be a workspace).\nconst getHookPackages = (): HookPackage[] => {\n const root = getRoot();\n const rootPkg = readPackageJson(root);\n\n const appDirs = getAvailableApps().map((appName) => ({ dir: getAppDirectory(appName), fallbackLabel: appName }));\n const workspaceDirs = (rootPkg?.workspaces ?? [])\n .flatMap((pattern) => expandWorkspacePattern(root, pattern))\n .map((dir) => ({ dir, fallbackLabel: path.relative(root, dir) }));\n\n const packages: HookPackage[] = [];\n const seen = new Set<string>();\n for (const { dir, fallbackLabel } of [...appDirs, ...workspaceDirs]) {\n const resolved = path.resolve(dir);\n if (seen.has(resolved)) {\n continue;\n }\n seen.add(resolved);\n\n const pkg = readPackageJson(dir);\n if (pkg) {\n packages.push({ dir, label: pkg.name ?? fallbackLabel, pkg });\n }\n }\n\n return packages;\n};\n\n// The script a hook resolves to in one package: `qpq:<hook>` when defined,\n// otherwise the plain `<hook>` script. Empty-string scripts count as absent.\nconst resolveHookScript = (pkg: HookPackageJson, hook: string): string | undefined => {\n if (pkg.scripts?.[`qpq:${hook}`]) {\n return `qpq:${hook}`;\n }\n if (pkg.scripts?.[hook]) {\n return hook;\n }\n return undefined;\n};\n\nconst getDeclaredDependencyNames = (pkg: HookPackageJson): string[] =>\n Object.keys({ ...pkg.dependencies, ...pkg.devDependencies, ...pkg.peerDependencies });\n\n// True for a package inside apps/<some other app>/. Packages outside apps/\n// are shared, so they never count as another app's.\nconst isOtherAppPackage = (dir: string, appName: string): boolean => {\n const relative = path.relative(getAppsDirectory(), path.resolve(dir));\n if (relative === '' || relative.startsWith('..') || path.isAbsolute(relative)) {\n return false;\n }\n return relative.split(path.sep)[0] !== appName;\n};\n\n// The app's own packages and the shared ones, plus every package they depend\n// on wherever it lives, so a cross-app dependency still gets built.\nconst getAppHookPackages = (appName: string): HookPackage[] => {\n const allPackages = getHookPackages();\n const byName = new Map<string, HookPackage>();\n for (const hookPackage of allPackages) {\n if (hookPackage.pkg.name) {\n byName.set(hookPackage.pkg.name, hookPackage);\n }\n }\n\n const selected = new Set(allPackages.filter((hookPackage) => !isOtherAppPackage(hookPackage.dir, appName)));\n const pending = [...selected];\n for (let current = pending.pop(); current; current = pending.pop()) {\n for (const depName of getDeclaredDependencyNames(current.pkg)) {\n const dep = byName.get(depName);\n if (dep && !selected.has(dep)) {\n selected.add(dep);\n pending.push(dep);\n }\n }\n }\n\n return allPackages.filter((hookPackage) => selected.has(hookPackage));\n};\n\n// Run the hook's script in each of `candidates` that defines it (qpq:<hook>\n// preferred over a plain <hook> script), in dependency order (declared deps\n// resolved by package name), with up to `jobs` running concurrently. Packages\n// whose dependencies aren't part of this run don't wait on them.\nconst runHookForPackages = async (candidates: HookPackage[], hook: string, jobs: number): Promise<TaskFailure[]> => {\n const hookPackages = candidates\n .map((candidate) => ({ ...candidate, scriptName: resolveHookScript(candidate.pkg, hook) }))\n .filter((candidate): candidate is HookPackage & { scriptName: string } => candidate.scriptName !== undefined);\n\n if (hookPackages.length === 0) {\n console.log(`No app or workspace package defines a \"qpq:${hook}\" or \"${hook}\" script, nothing to run.`);\n return [];\n }\n\n // Dependency edges only count between packages that are part of this run:\n // a dep that doesn't define the hook has nothing to wait for.\n const runSetNames = new Set(hookPackages.map((hookPackage) => hookPackage.pkg.name).filter(Boolean));\n\n const tasks = hookPackages.map((hookPackage) => ({\n id: hookPackage.pkg.name ?? hookPackage.dir,\n label: hookPackage.label,\n deps: getDeclaredDependencyNames(hookPackage.pkg).filter((name) => runSetNames.has(name) && name !== hookPackage.pkg.name),\n run: () => runCommandPrefixed(hookPackage.label, 'npm', ['run', hookPackage.scriptName], { cwd: hookPackage.dir, color: nextPrefixColor() }),\n }));\n\n console.log(`Running ${hook} hooks in ${tasks.length} package(s) (${jobs} jobs, dependency ordered)`);\n return runDependencyTasks(tasks, jobs);\n};\n\n/** Runs a hook in every app and workspace package that defines it. Returns the failures; callers decide fatality. */\nexport const runHookForAllPackages = (hook: string, jobs: number): Promise<TaskFailure[]> => runHookForPackages(getHookPackages(), hook, jobs);\n\n/**\n * Runs a hook in one app's packages, the shared packages outside apps/, and anything those depend on.\n * Other apps' packages are skipped. Returns the failures; callers decide fatality.\n */\nexport const runHookForAppPackages = (hook: string, appName: string, jobs: number): Promise<TaskFailure[]> =>\n runHookForPackages(getAppHookPackages(appName), hook, jobs);\n"]}
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../src/lib/hooks.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport path from 'path';\n\nimport { getAppDirectory, getAppsDirectory, getAvailableApps, getRoot } from './discovery';\nimport { nextPrefixColor, runCommand, runCommandPrefixed } from './runCommand';\nimport { runDependencyTasks, TaskFailure } from './runTasks';\nimport { createOutput } from './out';\n\nconst out = createOutput('hooks');\n\ntype HookPackageJson = {\n name?: string;\n scripts?: Record<string, string>;\n workspaces?: string[];\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n};\n\n// A directory whose package.json can carry qpq:<hook> scripts: either an app\n// (apps/<app>/package.json, which need not be an npm workspace) or any package\n// matched by the consumer root's `workspaces` globs.\ntype HookPackage = {\n dir: string;\n label: string; // app name or package name, for display\n pkg: HookPackageJson;\n};\n\n// App lifecycle hooks: apps/<app>/package.json scripts named `qpq:<hook>`\n// (e.g. qpq:postinstall, qpq:prebuild). The CLI invokes them when present;\n// apps without the hook are skipped silently.\nexport const runAppHook = async (appName: string, hook: string): Promise<boolean> => {\n const packageJsonPath = path.join(getAppDirectory(appName), 'package.json');\n if (!fs.existsSync(packageJsonPath)) {\n return false;\n }\n\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));\n const scriptName = `qpq:${hook}`;\n if (!packageJson.scripts?.[scriptName]) {\n return false;\n }\n\n out.log(`Running ${scriptName} for [${appName}]`);\n await runCommand('npm', ['run', scriptName], { cwd: getAppDirectory(appName) });\n return true;\n};\n\nconst isDirectory = (candidate: string): boolean => fs.existsSync(candidate) && fs.statSync(candidate).isDirectory();\n\nconst listSubdirectories = (dir: string): string[] => {\n if (!isDirectory(dir)) {\n return [];\n }\n return fs\n .readdirSync(dir)\n .map((name) => path.join(dir, name))\n .filter(isDirectory);\n};\n\n// Expand one `workspaces` glob. Only literal segments and bare `*` are\n// supported, which covers the patterns qpq repos use (e.g.\n// \"apps/*/services/*/models\"); anything fancier (\"**\", braces) matches nothing.\nconst expandWorkspacePattern = (root: string, pattern: string): string[] => {\n let dirs = [root];\n for (const segment of pattern.split('/')) {\n if (segment === '*') {\n dirs = dirs.flatMap(listSubdirectories);\n } else {\n dirs = dirs.map((dir) => path.join(dir, segment)).filter(isDirectory);\n }\n }\n return dirs;\n};\n\nconst readPackageJson = (dir: string): HookPackageJson | undefined => {\n const packageJsonPath = path.join(dir, 'package.json');\n if (!fs.existsSync(packageJsonPath)) {\n return undefined;\n }\n return JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')) as HookPackageJson;\n};\n\n// Every directory that can carry hooks: app dirs plus all workspace packages,\n// deduped (an app dir could itself be a workspace).\nconst getHookPackages = (): HookPackage[] => {\n const root = getRoot();\n const rootPkg = readPackageJson(root);\n\n const appDirs = getAvailableApps().map((appName) => ({ dir: getAppDirectory(appName), fallbackLabel: appName }));\n const workspaceDirs = (rootPkg?.workspaces ?? [])\n .flatMap((pattern) => expandWorkspacePattern(root, pattern))\n .map((dir) => ({ dir, fallbackLabel: path.relative(root, dir) }));\n\n const packages: HookPackage[] = [];\n const seen = new Set<string>();\n for (const { dir, fallbackLabel } of [...appDirs, ...workspaceDirs]) {\n const resolved = path.resolve(dir);\n if (seen.has(resolved)) {\n continue;\n }\n seen.add(resolved);\n\n const pkg = readPackageJson(dir);\n if (pkg) {\n packages.push({ dir, label: pkg.name ?? fallbackLabel, pkg });\n }\n }\n\n return packages;\n};\n\n// The script a hook resolves to in one package: `qpq:<hook>` when defined,\n// otherwise the plain `<hook>` script. Empty-string scripts count as absent.\nconst resolveHookScript = (pkg: HookPackageJson, hook: string): string | undefined => {\n if (pkg.scripts?.[`qpq:${hook}`]) {\n return `qpq:${hook}`;\n }\n if (pkg.scripts?.[hook]) {\n return hook;\n }\n return undefined;\n};\n\nconst getDeclaredDependencyNames = (pkg: HookPackageJson): string[] =>\n Object.keys({ ...pkg.dependencies, ...pkg.devDependencies, ...pkg.peerDependencies });\n\n// True for a package inside apps/<some other app>/. Packages outside apps/\n// are shared, so they never count as another app's.\nconst isOtherAppPackage = (dir: string, appName: string): boolean => {\n const relative = path.relative(getAppsDirectory(), path.resolve(dir));\n if (relative === '' || relative.startsWith('..') || path.isAbsolute(relative)) {\n return false;\n }\n return relative.split(path.sep)[0] !== appName;\n};\n\n// The app's own packages and the shared ones, plus every package they depend\n// on wherever it lives, so a cross-app dependency still gets built.\nconst getAppHookPackages = (appName: string): HookPackage[] => {\n const allPackages = getHookPackages();\n const byName = new Map<string, HookPackage>();\n for (const hookPackage of allPackages) {\n if (hookPackage.pkg.name) {\n byName.set(hookPackage.pkg.name, hookPackage);\n }\n }\n\n const selected = new Set(allPackages.filter((hookPackage) => !isOtherAppPackage(hookPackage.dir, appName)));\n const pending = [...selected];\n for (let current = pending.pop(); current; current = pending.pop()) {\n for (const depName of getDeclaredDependencyNames(current.pkg)) {\n const dep = byName.get(depName);\n if (dep && !selected.has(dep)) {\n selected.add(dep);\n pending.push(dep);\n }\n }\n }\n\n return allPackages.filter((hookPackage) => selected.has(hookPackage));\n};\n\n// Run the hook's script in each of `candidates` that defines it (qpq:<hook>\n// preferred over a plain <hook> script), in dependency order (declared deps\n// resolved by package name), with up to `jobs` running concurrently. Packages\n// whose dependencies aren't part of this run don't wait on them.\nconst runHookForPackages = async (candidates: HookPackage[], hook: string, jobs: number): Promise<TaskFailure[]> => {\n const hookPackages = candidates\n .map((candidate) => ({ ...candidate, scriptName: resolveHookScript(candidate.pkg, hook) }))\n .filter((candidate): candidate is HookPackage & { scriptName: string } => candidate.scriptName !== undefined);\n\n if (hookPackages.length === 0) {\n out.log(`No app or workspace package defines a \"qpq:${hook}\" or \"${hook}\" script, nothing to run.`);\n return [];\n }\n\n // Dependency edges only count between packages that are part of this run:\n // a dep that doesn't define the hook has nothing to wait for.\n const runSetNames = new Set(hookPackages.map((hookPackage) => hookPackage.pkg.name).filter(Boolean));\n\n const tasks = hookPackages.map((hookPackage) => ({\n id: hookPackage.pkg.name ?? hookPackage.dir,\n label: hookPackage.label,\n deps: getDeclaredDependencyNames(hookPackage.pkg).filter((name) => runSetNames.has(name) && name !== hookPackage.pkg.name),\n run: () => runCommandPrefixed(hookPackage.label, 'npm', ['run', hookPackage.scriptName], { cwd: hookPackage.dir, color: nextPrefixColor() }),\n }));\n\n out.log(`Running ${hook} hooks in ${tasks.length} package(s) (${jobs} jobs, dependency ordered)`);\n return runDependencyTasks(tasks, jobs);\n};\n\n/** Runs a hook in every app and workspace package that defines it. Returns the failures; callers decide fatality. */\nexport const runHookForAllPackages = (hook: string, jobs: number): Promise<TaskFailure[]> => runHookForPackages(getHookPackages(), hook, jobs);\n\n/**\n * Runs a hook in one app's packages, the shared packages outside apps/, and anything those depend on.\n * Other apps' packages are skipped. Returns the failures; callers decide fatality.\n */\nexport const runHookForAppPackages = (hook: string, appName: string, jobs: number): Promise<TaskFailure[]> =>\n runHookForPackages(getAppHookPackages(appName), hook, jobs);\n"]}
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.killChildWithEscalation = void 0;
4
+ const out_1 = require("./out");
5
+ const out = (0, out_1.createOutput)('kill-child-with-escalation');
4
6
  // Above the dev server's 5s worst-case shutdown budget, below docker's 10s
5
7
  // default stop grace, so a container stop still exits cleanly rather than
6
8
  // being SIGKILLed by docker mid-escalation.
@@ -19,7 +21,7 @@ const killChildWithEscalation = (child, graceMs = DEFAULT_KILL_GRACE_MS) => {
19
21
  }
20
22
  return new Promise((resolve) => {
21
23
  const timer = setTimeout(() => {
22
- console.warn(`Dev server did not exit within ${graceMs}ms — sending SIGKILL.`);
24
+ out.warn(`Dev server did not exit within ${graceMs}ms — sending SIGKILL.`);
23
25
  child.kill('SIGKILL');
24
26
  }, graceMs);
25
27
  child.once('exit', () => {
@@ -1 +1 @@
1
- {"version":3,"file":"killChildWithEscalation.js","sourceRoot":"","sources":["../../../src/lib/killChildWithEscalation.ts"],"names":[],"mappings":";;;AAWA,2EAA2E;AAC3E,0EAA0E;AAC1E,4CAA4C;AAC5C,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAEnC;;;;;;;GAOG;AACI,MAAM,uBAAuB,GAAG,CAAC,KAAmB,EAAE,OAAO,GAAG,qBAAqB,EAAiB,EAAE;IAC7G,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QACzD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,OAAO,CAAC,IAAI,CAAC,kCAAkC,OAAO,uBAAuB,CAAC,CAAC;YAC/E,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC,EAAE,OAAO,CAAC,CAAC;QAEZ,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;YACtB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAlBW,QAAA,uBAAuB,2BAkBlC","sourcesContent":["// Stopping the dev server child, with a hard backstop.\n//\n// The dev server now runs an ordered shutdown on SIGTERM (drain in-flight\n// work, then checkpoint the stores) instead of dying instantly. That is the\n// point - it is what stops a save-triggered restart eating a queue message\n// mid-story - but it means a bare `child.kill()` followed by waiting for exit\n// can now wait forever if a teardown wedges. Without escalation this would\n// trade a data-loss bug for a hung-restart bug, and the next launch would find\n// its ports still held.\nimport { ChildProcess } from 'child_process';\n\n// Above the dev server's 5s worst-case shutdown budget, below docker's 10s\n// default stop grace, so a container stop still exits cleanly rather than\n// being SIGKILLed by docker mid-escalation.\nconst DEFAULT_KILL_GRACE_MS = 6000;\n\n/**\n * SIGTERM the child, resolve once it has actually exited, and SIGKILL it if it\n * has not gone within the grace window.\n *\n * Resolves rather than rejects on escalation: the caller's next move is the\n * same either way (start the replacement), and a rejection would only give\n * every call site a catch to write.\n */\nexport const killChildWithEscalation = (child: ChildProcess, graceMs = DEFAULT_KILL_GRACE_MS): Promise<void> => {\n if (child.exitCode !== null || child.signalCode !== null) {\n return Promise.resolve();\n }\n\n return new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n console.warn(`Dev server did not exit within ${graceMs}ms — sending SIGKILL.`);\n child.kill('SIGKILL');\n }, graceMs);\n\n child.once('exit', () => {\n clearTimeout(timer);\n resolve();\n });\n\n child.kill('SIGTERM');\n });\n};\n"]}
1
+ {"version":3,"file":"killChildWithEscalation.js","sourceRoot":"","sources":["../../../src/lib/killChildWithEscalation.ts"],"names":[],"mappings":";;;AAUA,+BAAqC;AAErC,MAAM,GAAG,GAAG,IAAA,kBAAY,EAAC,4BAA4B,CAAC,CAAC;AAEvD,2EAA2E;AAC3E,0EAA0E;AAC1E,4CAA4C;AAC5C,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAEnC;;;;;;;GAOG;AACI,MAAM,uBAAuB,GAAG,CAAC,KAAmB,EAAE,OAAO,GAAG,qBAAqB,EAAiB,EAAE;IAC7G,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QACzD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,GAAG,CAAC,IAAI,CAAC,kCAAkC,OAAO,uBAAuB,CAAC,CAAC;YAC3E,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC,EAAE,OAAO,CAAC,CAAC;QAEZ,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;YACtB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAlBW,QAAA,uBAAuB,2BAkBlC","sourcesContent":["// Stopping the dev server child, with a hard backstop.\n//\n// The dev server now runs an ordered shutdown on SIGTERM (drain in-flight\n// work, then checkpoint the stores) instead of dying instantly. That is the\n// point - it is what stops a save-triggered restart eating a queue message\n// mid-story - but it means a bare `child.kill()` followed by waiting for exit\n// can now wait forever if a teardown wedges. Without escalation this would\n// trade a data-loss bug for a hung-restart bug, and the next launch would find\n// its ports still held.\nimport { ChildProcess } from 'child_process';\nimport { createOutput } from './out';\n\nconst out = createOutput('kill-child-with-escalation');\n\n// Above the dev server's 5s worst-case shutdown budget, below docker's 10s\n// default stop grace, so a container stop still exits cleanly rather than\n// being SIGKILLed by docker mid-escalation.\nconst DEFAULT_KILL_GRACE_MS = 6000;\n\n/**\n * SIGTERM the child, resolve once it has actually exited, and SIGKILL it if it\n * has not gone within the grace window.\n *\n * Resolves rather than rejects on escalation: the caller's next move is the\n * same either way (start the replacement), and a rejection would only give\n * every call site a catch to write.\n */\nexport const killChildWithEscalation = (child: ChildProcess, graceMs = DEFAULT_KILL_GRACE_MS): Promise<void> => {\n if (child.exitCode !== null || child.signalCode !== null) {\n return Promise.resolve();\n }\n\n return new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n out.warn(`Dev server did not exit within ${graceMs}ms — sending SIGKILL.`);\n child.kill('SIGKILL');\n }, graceMs);\n\n child.once('exit', () => {\n clearTimeout(timer);\n resolve();\n });\n\n child.kill('SIGTERM');\n });\n};\n"]}