@qpqjs/cli 0.3.0 → 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.
- package/lib/commonjs/cli/commandMenu.js +5 -4
- package/lib/commonjs/cli/commandMenu.js.map +1 -1
- package/lib/commonjs/cli/runCli.js +15 -7
- package/lib/commonjs/cli/runCli.js.map +1 -1
- package/lib/commonjs/commands/checkCircular.js +15 -7
- package/lib/commonjs/commands/checkCircular.js.map +1 -1
- package/lib/commonjs/commands/clearResources.js +8 -14
- package/lib/commonjs/commands/clearResources.js.map +1 -1
- package/lib/commonjs/commands/go.js +3 -7
- package/lib/commonjs/commands/go.js.map +1 -1
- package/lib/commonjs/commands/goDev.js +4 -2
- package/lib/commonjs/commands/goDev.js.map +1 -1
- package/lib/commonjs/commands/goDevApi.js +23 -20
- package/lib/commonjs/commands/goDevApi.js.map +1 -1
- package/lib/commonjs/commands/goDevWeb.js +13 -8
- package/lib/commonjs/commands/goDevWeb.js.map +1 -1
- package/lib/commonjs/commands/goDocker.js +10 -13
- package/lib/commonjs/commands/goDocker.js.map +1 -1
- package/lib/commonjs/commands/hooks.js +4 -2
- package/lib/commonjs/commands/hooks.js.map +1 -1
- package/lib/commonjs/commands/migrate.js +11 -9
- package/lib/commonjs/commands/migrate.js.map +1 -1
- package/lib/commonjs/commands/prep.js +4 -2
- package/lib/commonjs/commands/prep.js.map +1 -1
- package/lib/commonjs/commands/publish.js +4 -7
- package/lib/commonjs/commands/publish.js.map +1 -1
- package/lib/commonjs/commands/scriptBuild.js +7 -5
- package/lib/commonjs/commands/scriptBuild.js.map +1 -1
- package/lib/commonjs/commands/scriptBuilderLayer.js +6 -4
- package/lib/commonjs/commands/scriptBuilderLayer.js.map +1 -1
- package/lib/commonjs/commands/scriptNew.js +7 -5
- package/lib/commonjs/commands/scriptNew.js.map +1 -1
- package/lib/commonjs/commands/setup.js +14 -22
- package/lib/commonjs/commands/setup.js.map +1 -1
- package/lib/commonjs/commands/synth.js +16 -20
- package/lib/commonjs/commands/synth.js.map +1 -1
- package/lib/commonjs/commands/teardown.js +8 -14
- package/lib/commonjs/commands/teardown.js.map +1 -1
- package/lib/commonjs/lib/CliError.d.ts +4 -0
- package/lib/commonjs/lib/CliError.js +14 -0
- package/lib/commonjs/lib/CliError.js.map +1 -0
- package/lib/commonjs/lib/circularImports.d.ts +0 -1
- package/lib/commonjs/lib/circularImports.js +5 -6
- package/lib/commonjs/lib/circularImports.js.map +1 -1
- package/lib/commonjs/lib/clearResourcesPlan.d.ts +0 -1
- package/lib/commonjs/lib/clearResourcesPlan.js +12 -13
- package/lib/commonjs/lib/clearResourcesPlan.js.map +1 -1
- package/lib/commonjs/lib/deployPrompts.d.ts +2 -1
- package/lib/commonjs/lib/deployPrompts.js +16 -16
- package/lib/commonjs/lib/deployPrompts.js.map +1 -1
- package/lib/commonjs/lib/devServerEntry.js +5 -4
- package/lib/commonjs/lib/devServerEntry.js.map +1 -1
- package/lib/commonjs/lib/devServerPorts.js +2 -4
- package/lib/commonjs/lib/devServerPorts.js.map +1 -1
- package/lib/commonjs/lib/discovery.d.ts +0 -2
- package/lib/commonjs/lib/discovery.js +11 -44
- package/lib/commonjs/lib/discovery.js.map +1 -1
- package/lib/commonjs/lib/hooks.js +5 -3
- package/lib/commonjs/lib/hooks.js.map +1 -1
- package/lib/commonjs/lib/killChildWithEscalation.js +3 -1
- package/lib/commonjs/lib/killChildWithEscalation.js.map +1 -1
- package/lib/commonjs/lib/killStaleListeners.js +24 -37
- package/lib/commonjs/lib/killStaleListeners.js.map +1 -1
- package/lib/commonjs/lib/out.d.ts +11 -0
- package/lib/commonjs/lib/out.js +30 -0
- package/lib/commonjs/lib/out.js.map +1 -0
- package/lib/commonjs/lib/prompts.d.ts +1 -0
- package/lib/commonjs/lib/prompts.js +15 -2
- package/lib/commonjs/lib/prompts.js.map +1 -1
- package/lib/commonjs/lib/qpqConfigs.js +4 -4
- package/lib/commonjs/lib/qpqConfigs.js.map +1 -1
- package/lib/commonjs/lib/requireDriverCapability.d.ts +7 -0
- package/lib/commonjs/lib/requireDriverCapability.js +17 -0
- package/lib/commonjs/lib/requireDriverCapability.js.map +1 -0
- package/lib/commonjs/lib/resolveAppSelection.d.ts +1 -0
- package/lib/commonjs/lib/resolveAppSelection.js +19 -12
- package/lib/commonjs/lib/resolveAppSelection.js.map +1 -1
- package/lib/commonjs/lib/resolveCommandContext.d.ts +12 -0
- package/lib/commonjs/lib/resolveCommandContext.js +26 -0
- package/lib/commonjs/lib/resolveCommandContext.js.map +1 -0
- package/lib/commonjs/lib/resolveDeployment.d.ts +0 -1
- package/lib/commonjs/lib/resolveDeployment.js +15 -10
- package/lib/commonjs/lib/resolveDeployment.js.map +1 -1
- package/lib/commonjs/lib/runCommand.d.ts +20 -0
- package/lib/commonjs/lib/runCommand.js +64 -34
- package/lib/commonjs/lib/runCommand.js.map +1 -1
- package/lib/commonjs/lib/runTasks.js +8 -4
- package/lib/commonjs/lib/runTasks.js.map +1 -1
- package/lib/commonjs/lib/timing.js +5 -3
- package/lib/commonjs/lib/timing.js.map +1 -1
- package/lib/commonjs/lib/typeWatcher.d.ts +2 -1
- package/lib/commonjs/lib/typeWatcher.js +6 -13
- package/lib/commonjs/lib/typeWatcher.js.map +1 -1
- package/lib/commonjs/lib/userScriptBuilderLayer.d.ts +0 -2
- package/lib/commonjs/lib/userScriptBuilderLayer.js +7 -8
- package/lib/commonjs/lib/userScriptBuilderLayer.js.map +1 -1
- package/lib/commonjs/lib/views.js +3 -2
- package/lib/commonjs/lib/views.js.map +1 -1
- package/lib/commonjs/lib/webAddressingEnv.d.ts +0 -2
- package/lib/commonjs/lib/webAddressingEnv.js +4 -4
- package/lib/commonjs/lib/webAddressingEnv.js.map +1 -1
- package/lib/commonjs/platforms/aws/awsCredentials.js +3 -1
- package/lib/commonjs/platforms/aws/awsCredentials.js.map +1 -1
- package/lib/commonjs/platforms/aws/cdkApp.js +1 -1
- package/lib/commonjs/platforms/aws/cdkApp.js.map +1 -1
- package/lib/commonjs/platforms/aws/clearResources.js +16 -26
- package/lib/commonjs/platforms/aws/clearResources.js.map +1 -1
- package/lib/commonjs/platforms/aws/getDeployEnv.d.ts +1 -0
- package/lib/commonjs/platforms/aws/getDeployEnv.js +12 -0
- package/lib/commonjs/platforms/aws/getDeployEnv.js.map +1 -0
- package/lib/commonjs/platforms/aws/go.js +17 -15
- package/lib/commonjs/platforms/aws/go.js.map +1 -1
- package/lib/commonjs/platforms/aws/goDocker.js +29 -26
- package/lib/commonjs/platforms/aws/goDocker.js.map +1 -1
- package/lib/commonjs/platforms/aws/goDockerManifest.js +6 -1
- package/lib/commonjs/platforms/aws/goDockerManifest.js.map +1 -1
- package/lib/commonjs/platforms/aws/publish.js +13 -11
- package/lib/commonjs/platforms/aws/publish.js.map +1 -1
- package/lib/commonjs/platforms/aws/remote.js +8 -7
- package/lib/commonjs/platforms/aws/remote.js.map +1 -1
- package/lib/commonjs/platforms/aws/setup/buildBucketNamesStep.js +4 -4
- package/lib/commonjs/platforms/aws/setup/buildBucketNamesStep.js.map +1 -1
- package/lib/commonjs/platforms/aws/setup/buildGithubDeployRoleStep.js +4 -3
- package/lib/commonjs/platforms/aws/setup/buildGithubDeployRoleStep.js.map +1 -1
- package/lib/commonjs/platforms/aws/stacks.d.ts +0 -1
- package/lib/commonjs/platforms/aws/stacks.js +21 -33
- package/lib/commonjs/platforms/aws/stacks.js.map +1 -1
- package/lib/commonjs/platforms/aws/teardown.js +9 -7
- package/lib/commonjs/platforms/aws/teardown.js.map +1 -1
- package/lib/commonjs/platforms/aws/viewsSync.js +23 -11
- package/lib/commonjs/platforms/aws/viewsSync.js.map +1 -1
- package/lib/commonjs/platforms/docker/go.js +20 -18
- package/lib/commonjs/platforms/docker/go.js.map +1 -1
- package/lib/commonjs/platforms/docker/index.js +5 -4
- package/lib/commonjs/platforms/docker/index.js.map +1 -1
- package/lib/commonjs/platforms/docker/parseMounts.js +3 -2
- package/lib/commonjs/platforms/docker/parseMounts.js.map +1 -1
- package/lib/commonjs/platforms/index.js +4 -2
- package/lib/commonjs/platforms/index.js.map +1 -1
- package/lib/commonjs/platforms/types.d.ts +2 -2
- package/lib/commonjs/platforms/types.js.map +1 -1
- package/lib/esm/cli/commandMenu.js +5 -4
- package/lib/esm/cli/commandMenu.js.map +1 -1
- package/lib/esm/cli/runCli.js +15 -7
- package/lib/esm/cli/runCli.js.map +1 -1
- package/lib/esm/commands/checkCircular.js +15 -7
- package/lib/esm/commands/checkCircular.js.map +1 -1
- package/lib/esm/commands/clearResources.js +8 -14
- package/lib/esm/commands/clearResources.js.map +1 -1
- package/lib/esm/commands/go.js +3 -7
- package/lib/esm/commands/go.js.map +1 -1
- package/lib/esm/commands/goDev.js +4 -2
- package/lib/esm/commands/goDev.js.map +1 -1
- package/lib/esm/commands/goDevApi.js +21 -18
- package/lib/esm/commands/goDevApi.js.map +1 -1
- package/lib/esm/commands/goDevWeb.js +12 -8
- package/lib/esm/commands/goDevWeb.js.map +1 -1
- package/lib/esm/commands/goDocker.js +10 -13
- package/lib/esm/commands/goDocker.js.map +1 -1
- package/lib/esm/commands/hooks.js +4 -2
- package/lib/esm/commands/hooks.js.map +1 -1
- package/lib/esm/commands/migrate.js +9 -7
- package/lib/esm/commands/migrate.js.map +1 -1
- package/lib/esm/commands/prep.js +4 -2
- package/lib/esm/commands/prep.js.map +1 -1
- package/lib/esm/commands/publish.js +4 -7
- package/lib/esm/commands/publish.js.map +1 -1
- package/lib/esm/commands/scriptBuild.js +7 -5
- package/lib/esm/commands/scriptBuild.js.map +1 -1
- package/lib/esm/commands/scriptBuilderLayer.js +6 -4
- package/lib/esm/commands/scriptBuilderLayer.js.map +1 -1
- package/lib/esm/commands/scriptNew.js +7 -5
- package/lib/esm/commands/scriptNew.js.map +1 -1
- package/lib/esm/commands/setup.js +14 -22
- package/lib/esm/commands/setup.js.map +1 -1
- package/lib/esm/commands/synth.js +18 -22
- package/lib/esm/commands/synth.js.map +1 -1
- package/lib/esm/commands/teardown.js +8 -14
- package/lib/esm/commands/teardown.js.map +1 -1
- package/lib/esm/lib/CliError.d.ts +4 -0
- package/lib/esm/lib/CliError.js +11 -0
- package/lib/esm/lib/CliError.js.map +1 -0
- package/lib/esm/lib/circularImports.d.ts +0 -1
- package/lib/esm/lib/circularImports.js +4 -4
- package/lib/esm/lib/circularImports.js.map +1 -1
- package/lib/esm/lib/clearResourcesPlan.d.ts +0 -1
- package/lib/esm/lib/clearResourcesPlan.js +12 -12
- package/lib/esm/lib/clearResourcesPlan.js.map +1 -1
- package/lib/esm/lib/deployPrompts.d.ts +2 -1
- package/lib/esm/lib/deployPrompts.js +17 -17
- package/lib/esm/lib/deployPrompts.js.map +1 -1
- package/lib/esm/lib/devServerEntry.js +5 -4
- package/lib/esm/lib/devServerEntry.js.map +1 -1
- package/lib/esm/lib/devServerPorts.js +2 -3
- package/lib/esm/lib/devServerPorts.js.map +1 -1
- package/lib/esm/lib/discovery.d.ts +0 -2
- package/lib/esm/lib/discovery.js +10 -38
- package/lib/esm/lib/discovery.js.map +1 -1
- package/lib/esm/lib/hooks.js +5 -3
- package/lib/esm/lib/hooks.js.map +1 -1
- package/lib/esm/lib/killChildWithEscalation.js +3 -1
- package/lib/esm/lib/killChildWithEscalation.js.map +1 -1
- package/lib/esm/lib/killStaleListeners.js +24 -37
- package/lib/esm/lib/killStaleListeners.js.map +1 -1
- package/lib/esm/lib/out.d.ts +11 -0
- package/lib/esm/lib/out.js +26 -0
- package/lib/esm/lib/out.js.map +1 -0
- package/lib/esm/lib/prompts.d.ts +1 -0
- package/lib/esm/lib/prompts.js +13 -1
- package/lib/esm/lib/prompts.js.map +1 -1
- package/lib/esm/lib/qpqConfigs.js +4 -4
- package/lib/esm/lib/qpqConfigs.js.map +1 -1
- package/lib/esm/lib/requireDriverCapability.d.ts +7 -0
- package/lib/esm/lib/requireDriverCapability.js +13 -0
- package/lib/esm/lib/requireDriverCapability.js.map +1 -0
- package/lib/esm/lib/resolveAppSelection.d.ts +1 -0
- package/lib/esm/lib/resolveAppSelection.js +17 -11
- package/lib/esm/lib/resolveAppSelection.js.map +1 -1
- package/lib/esm/lib/resolveCommandContext.d.ts +12 -0
- package/lib/esm/lib/resolveCommandContext.js +12 -0
- package/lib/esm/lib/resolveCommandContext.js.map +1 -0
- package/lib/esm/lib/resolveDeployment.d.ts +0 -1
- package/lib/esm/lib/resolveDeployment.js +13 -8
- package/lib/esm/lib/resolveDeployment.js.map +1 -1
- package/lib/esm/lib/runCommand.d.ts +20 -0
- package/lib/esm/lib/runCommand.js +52 -31
- package/lib/esm/lib/runCommand.js.map +1 -1
- package/lib/esm/lib/runTasks.js +8 -4
- package/lib/esm/lib/runTasks.js.map +1 -1
- package/lib/esm/lib/timing.js +5 -3
- package/lib/esm/lib/timing.js.map +1 -1
- package/lib/esm/lib/typeWatcher.d.ts +2 -1
- package/lib/esm/lib/typeWatcher.js +6 -13
- package/lib/esm/lib/typeWatcher.js.map +1 -1
- package/lib/esm/lib/userScriptBuilderLayer.d.ts +0 -2
- package/lib/esm/lib/userScriptBuilderLayer.js +5 -5
- package/lib/esm/lib/userScriptBuilderLayer.js.map +1 -1
- package/lib/esm/lib/views.js +3 -2
- package/lib/esm/lib/views.js.map +1 -1
- package/lib/esm/lib/webAddressingEnv.d.ts +0 -2
- package/lib/esm/lib/webAddressingEnv.js +1 -1
- package/lib/esm/lib/webAddressingEnv.js.map +1 -1
- package/lib/esm/platforms/aws/awsCredentials.js +3 -1
- package/lib/esm/platforms/aws/awsCredentials.js.map +1 -1
- package/lib/esm/platforms/aws/cdkApp.js +1 -1
- package/lib/esm/platforms/aws/cdkApp.js.map +1 -1
- package/lib/esm/platforms/aws/clearResources.js +16 -25
- package/lib/esm/platforms/aws/clearResources.js.map +1 -1
- package/lib/esm/platforms/aws/getDeployEnv.d.ts +1 -0
- package/lib/esm/platforms/aws/getDeployEnv.js +8 -0
- package/lib/esm/platforms/aws/getDeployEnv.js.map +1 -0
- package/lib/esm/platforms/aws/go.js +17 -15
- package/lib/esm/platforms/aws/go.js.map +1 -1
- package/lib/esm/platforms/aws/goDocker.js +29 -26
- package/lib/esm/platforms/aws/goDocker.js.map +1 -1
- package/lib/esm/platforms/aws/goDockerManifest.js +6 -1
- package/lib/esm/platforms/aws/goDockerManifest.js.map +1 -1
- package/lib/esm/platforms/aws/publish.js +13 -11
- package/lib/esm/platforms/aws/publish.js.map +1 -1
- package/lib/esm/platforms/aws/remote.js +9 -7
- package/lib/esm/platforms/aws/remote.js.map +1 -1
- package/lib/esm/platforms/aws/setup/buildBucketNamesStep.js +2 -2
- package/lib/esm/platforms/aws/setup/buildBucketNamesStep.js.map +1 -1
- package/lib/esm/platforms/aws/setup/buildGithubDeployRoleStep.js +4 -3
- package/lib/esm/platforms/aws/setup/buildGithubDeployRoleStep.js.map +1 -1
- package/lib/esm/platforms/aws/stacks.d.ts +0 -1
- package/lib/esm/platforms/aws/stacks.js +22 -30
- package/lib/esm/platforms/aws/stacks.js.map +1 -1
- package/lib/esm/platforms/aws/teardown.js +9 -7
- package/lib/esm/platforms/aws/teardown.js.map +1 -1
- package/lib/esm/platforms/aws/viewsSync.js +22 -10
- package/lib/esm/platforms/aws/viewsSync.js.map +1 -1
- package/lib/esm/platforms/docker/go.js +21 -19
- package/lib/esm/platforms/docker/go.js.map +1 -1
- package/lib/esm/platforms/docker/index.js +5 -4
- package/lib/esm/platforms/docker/index.js.map +1 -1
- package/lib/esm/platforms/docker/parseMounts.js +3 -2
- package/lib/esm/platforms/docker/parseMounts.js.map +1 -1
- package/lib/esm/platforms/index.js +4 -2
- package/lib/esm/platforms/index.js.map +1 -1
- package/lib/esm/platforms/types.d.ts +2 -2
- package/lib/esm/platforms/types.js.map +1 -1
- package/package.json +28 -15
- package/templates/user-script/README.md +1 -1
|
@@ -1,20 +1,14 @@
|
|
|
1
|
-
// `qpq teardown
|
|
1
|
+
// `qpq teardown`, interactive destroy, the counterpart of `qpq go`. Resolves
|
|
2
2
|
// the app + deployment the same way, asks which services to tear down (with
|
|
3
|
-
// a typed confirmation
|
|
3
|
+
// a typed confirmation, this deletes stored data), then hands off to the
|
|
4
4
|
// platform driver to destroy the web, api and inf stacks in that order.
|
|
5
5
|
import { promptTeardownPlan } from '../lib/deployPrompts';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { getPlatformDriver } from '../platforms';
|
|
6
|
+
import { requireDriverCapability } from '../lib/requireDriverCapability';
|
|
7
|
+
import { resolveCommandContext } from '../lib/resolveCommandContext';
|
|
9
8
|
export const teardownCommand = async (argv) => {
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
console.error(`The '${deployment.platform}' platform has no teardown strategy.`);
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
const plan = await promptTeardownPlan(appName);
|
|
18
|
-
await driver.teardown(appName, plan);
|
|
9
|
+
const context = await resolveCommandContext(argv);
|
|
10
|
+
const teardown = requireDriverCapability(context, 'teardown');
|
|
11
|
+
const plan = await promptTeardownPlan(context.appName);
|
|
12
|
+
await teardown(context.appName, plan);
|
|
19
13
|
};
|
|
20
14
|
//# sourceMappingURL=teardown.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teardown.js","sourceRoot":"","sources":["../../../src/commands/teardown.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"teardown.js","sourceRoot":"","sources":["../../../src/commands/teardown.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,4EAA4E;AAC5E,yEAAyE;AACzE,wEAAwE;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,IAAc,EAAiB,EAAE;IACrE,MAAM,OAAO,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,uBAAuB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAE9D,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvD,MAAM,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AACxC,CAAC,CAAC","sourcesContent":["// `qpq teardown`, interactive destroy, the counterpart of `qpq go`. Resolves\n// the app + deployment the same way, asks which services to tear down (with\n// a typed confirmation, this deletes stored data), then hands off to the\n// platform driver to destroy the web, api and inf stacks in that order.\nimport { promptTeardownPlan } from '../lib/deployPrompts';\nimport { requireDriverCapability } from '../lib/requireDriverCapability';\nimport { resolveCommandContext } from '../lib/resolveCommandContext';\n\nexport const teardownCommand = async (argv: string[]): Promise<void> => {\n const context = await resolveCommandContext(argv);\n const teardown = requireDriverCapability(context, 'teardown');\n\n const plan = await promptTeardownPlan(context.appName);\n\n await teardown(context.appName, plan);\n};\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// A failure the command layer turns into an exit code and one line of output. Library code
|
|
2
|
+
// throws this instead of printing and exiting itself, so it stays usable from anywhere.
|
|
3
|
+
export class CliError extends Error {
|
|
4
|
+
exitCode;
|
|
5
|
+
constructor(message, exitCode = 1) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'CliError';
|
|
8
|
+
this.exitCode = exitCode;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=CliError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CliError.js","sourceRoot":"","sources":["../../../src/lib/CliError.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,wFAAwF;AACxF,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,QAAQ,CAAS;IAE1B,YAAY,OAAe,EAAE,WAAmB,CAAC;QAC/C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF","sourcesContent":["// A failure the command layer turns into an exit code and one line of output. Library code\n// throws this instead of printing and exiting itself, so it stays usable from anywhere.\nexport class CliError extends Error {\n readonly exitCode: number;\n\n constructor(message: string, exitCode: number = 1) {\n super(message);\n this.name = 'CliError';\n this.exitCode = exitCode;\n }\n}\n"]}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// flows like `npm run validate-ts` with no bundling.
|
|
6
6
|
//
|
|
7
7
|
// Scope: each workspace's src, relative imports. Cross-package cycles are
|
|
8
|
-
// already impossible
|
|
8
|
+
// already impossible, TypeScript project references refuse to build them, so
|
|
9
9
|
// every runtime-relevant cycle lives inside one package's src. Type-only
|
|
10
10
|
// imports are skipped (skipTypeImports): tsc erases them, so they never create
|
|
11
11
|
// a runtime cycle.
|
|
@@ -14,7 +14,7 @@ import path from 'path';
|
|
|
14
14
|
// madge ships no type definitions; declare the narrow slice we use.
|
|
15
15
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
16
16
|
const madge = require('madge');
|
|
17
|
-
// Expand npm workspace globs (plain path segments and `*` segments only
|
|
17
|
+
// Expand npm workspace globs (plain path segments and `*` segments only, the
|
|
18
18
|
// only forms npm workspaces use in practice) into existing directories.
|
|
19
19
|
const expandWorkspaceGlob = (root, pattern) => {
|
|
20
20
|
let candidates = [root];
|
|
@@ -33,7 +33,7 @@ const expandWorkspaceGlob = (root, pattern) => {
|
|
|
33
33
|
}
|
|
34
34
|
return candidates.filter((dir) => fs.existsSync(dir) && fs.statSync(dir).isDirectory());
|
|
35
35
|
};
|
|
36
|
-
|
|
36
|
+
const getWorkspaceDirectories = (root) => {
|
|
37
37
|
const packageJson = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
38
38
|
const patterns = packageJson.workspaces ?? [];
|
|
39
39
|
return patterns.flatMap((pattern) => expandWorkspaceGlob(root, pattern));
|
|
@@ -44,7 +44,7 @@ export const findWorkspaceImportCycles = async (root) => {
|
|
|
44
44
|
.filter((dir) => fs.existsSync(dir));
|
|
45
45
|
// Excludes: node_modules and built output (dist/lib), declaration files
|
|
46
46
|
// (type-only, can't cycle at runtime), and anything madge resolves to outside
|
|
47
|
-
// the repo root (linked framework packages
|
|
47
|
+
// the repo root (linked framework packages, their cycles surface through the
|
|
48
48
|
// rspack CircularCheckRspackPlugin instead, this command owns the consumer
|
|
49
49
|
// repo). Out-of-root paths start with '..' because baseDir is the repo root.
|
|
50
50
|
const result = await madge(sourceDirs, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circularImports.js","sourceRoot":"","sources":["../../../src/lib/circularImports.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,+EAA+E;AAC/E,4EAA4E;AAC5E,8EAA8E;AAC9E,qDAAqD;AACrD,EAAE;AACF,0EAA0E;AAC1E
|
|
1
|
+
{"version":3,"file":"circularImports.js","sourceRoot":"","sources":["../../../src/lib/circularImports.ts"],"names":[],"mappings":"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","sourcesContent":["// Source-level circular import detection over the consumer repo's npm\n// workspaces, powered by madge (AST-based dependency graph + cycle detection).\n// Complements the rspack CircularCheckRspackPlugin (which needs a bundle to\n// run): this scans TypeScript sources directly, so it can run inside tsc-only\n// flows like `npm run validate-ts` with no bundling.\n//\n// Scope: each workspace's src, relative imports. Cross-package cycles are\n// already impossible, TypeScript project references refuse to build them, so\n// every runtime-relevant cycle lives inside one package's src. Type-only\n// imports are skipped (skipTypeImports): tsc erases them, so they never create\n// a runtime cycle.\nimport fs from 'fs';\nimport path from 'path';\n\n// madge ships no type definitions; declare the narrow slice we use.\n// eslint-disable-next-line @typescript-eslint/no-require-imports\nconst madge = require('madge') as (paths: string[], config: object) => Promise<{ circular(): string[][] }>;\n\nexport type ImportCycle = string[];\n\n// Expand npm workspace globs (plain path segments and `*` segments only, the\n// only forms npm workspaces use in practice) into existing directories.\nconst expandWorkspaceGlob = (root: string, pattern: string): string[] => {\n let candidates = [root];\n\n for (const segment of pattern.split('/')) {\n if (segment === '*') {\n candidates = candidates.flatMap((dir) =>\n fs.existsSync(dir)\n ? fs\n .readdirSync(dir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => path.join(dir, entry.name))\n : [],\n );\n } else {\n candidates = candidates.map((dir) => path.join(dir, segment));\n }\n }\n\n return candidates.filter((dir) => fs.existsSync(dir) && fs.statSync(dir).isDirectory());\n};\n\nconst getWorkspaceDirectories = (root: string): string[] => {\n const packageJson = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));\n const patterns: string[] = packageJson.workspaces ?? [];\n return patterns.flatMap((pattern) => expandWorkspaceGlob(root, pattern));\n};\n\nexport const findWorkspaceImportCycles = async (root: string): Promise<ImportCycle[]> => {\n const sourceDirs = getWorkspaceDirectories(root)\n .map((dir) => path.join(dir, 'src'))\n .filter((dir) => fs.existsSync(dir));\n\n // Excludes: node_modules and built output (dist/lib), declaration files\n // (type-only, can't cycle at runtime), and anything madge resolves to outside\n // the repo root (linked framework packages, their cycles surface through the\n // rspack CircularCheckRspackPlugin instead, this command owns the consumer\n // repo). Out-of-root paths start with '..' because baseDir is the repo root.\n const result = await madge(sourceDirs, {\n baseDir: root,\n fileExtensions: ['ts', 'tsx'],\n excludeRegExp: [/node_modules/, /(^|\\/)(dist|lib)\\//, /\\.d\\.ts$/, /^\\.\\./],\n detectiveOptions: {\n ts: { skipTypeImports: true },\n tsx: { skipTypeImports: true },\n },\n });\n\n return result.circular();\n};\n"]}
|
|
@@ -11,5 +11,4 @@ export type ClearResourcesPlan = {
|
|
|
11
11
|
appName: string;
|
|
12
12
|
resources: ClearableResource[];
|
|
13
13
|
};
|
|
14
|
-
export declare const getClearableResources: (appName: string) => ClearableResource[];
|
|
15
14
|
export declare const promptClearResourcesPlan: (appName: string) => Promise<ClearResourcesPlan>;
|
|
@@ -7,18 +7,20 @@
|
|
|
7
7
|
// the service that owns it.
|
|
8
8
|
import { qpqCoreUtils } from '@qpqjs/core';
|
|
9
9
|
import { getServiceNames } from './discovery';
|
|
10
|
-
import { promptCheckbox, promptSelect, promptText } from './prompts';
|
|
11
|
-
import { loadServiceQpqConfig } from './qpqConfigs';
|
|
12
10
|
import { getDeployTargetLabel } from './getDeployTargetLabel';
|
|
11
|
+
import { promptCheckbox, promptSelect, promptTypedConfirmation } from './prompts';
|
|
12
|
+
import { loadServiceQpqConfig } from './qpqConfigs';
|
|
13
|
+
import { createOutput } from './out';
|
|
14
|
+
const out = createOutput('clear-resources-plan');
|
|
13
15
|
// Every owned data resource across the app's services, in service order.
|
|
14
|
-
|
|
16
|
+
const getClearableResources = (appName) => {
|
|
15
17
|
const resources = [];
|
|
16
18
|
for (const service of getServiceNames(appName)) {
|
|
17
19
|
const qpqConfig = loadServiceQpqConfig(appName, service);
|
|
18
|
-
for (const drive of qpqCoreUtils.
|
|
20
|
+
for (const drive of qpqCoreUtils.getOwnedStorageDriveConfigs(qpqConfig)) {
|
|
19
21
|
resources.push({ service, kind: 'storageDrive', resourceName: drive.storageDrive });
|
|
20
22
|
}
|
|
21
|
-
for (const store of qpqCoreUtils.
|
|
23
|
+
for (const store of qpqCoreUtils.getOwnedKeyValueStoreConfigs(qpqConfig)) {
|
|
22
24
|
resources.push({ service, kind: 'keyValueStore', resourceName: store.keyValueStoreName });
|
|
23
25
|
}
|
|
24
26
|
}
|
|
@@ -42,22 +44,20 @@ export const promptClearResourcesPlan = async (appName) => {
|
|
|
42
44
|
const deployInfo = getDeployTargetLabel();
|
|
43
45
|
const clearable = getClearableResources(appName);
|
|
44
46
|
if (clearable.length === 0) {
|
|
45
|
-
|
|
47
|
+
out.log('No storage drives or key value stores found in this app.');
|
|
46
48
|
return { kind: 'cancelled' };
|
|
47
49
|
}
|
|
48
50
|
const mode = await promptSelect('How do you want to select what to empty?', [BY_SERVICE, BY_RESOURCE]);
|
|
49
51
|
const resources = mode === BY_SERVICE ? await pickByService(clearable) : await pickByResource(clearable);
|
|
50
52
|
if (resources.length === 0) {
|
|
51
|
-
|
|
53
|
+
out.log('Nothing selected. Exiting!');
|
|
52
54
|
return { kind: 'cancelled' };
|
|
53
55
|
}
|
|
54
|
-
|
|
56
|
+
out.log(`\nThis will EMPTY (delete all stored data from) ${resources.length} resource(s) in [${deployInfo}]:`);
|
|
55
57
|
for (const resource of resources) {
|
|
56
|
-
|
|
58
|
+
out.log(` ${resource.service} — ${resource.resourceName} (${resource.kind === 'storageDrive' ? 'bucket' : 'table'})`);
|
|
57
59
|
}
|
|
58
|
-
|
|
59
|
-
if (confirmation !== deployInfo) {
|
|
60
|
-
console.log('Confirmation did not match. Exiting!');
|
|
60
|
+
if (!(await promptTypedConfirmation(deployInfo))) {
|
|
61
61
|
return { kind: 'cancelled' };
|
|
62
62
|
}
|
|
63
63
|
return { kind: 'resources', appName, resources };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clearResourcesPlan.js","sourceRoot":"","sources":["../../../src/lib/clearResourcesPlan.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,yEAAyE;AACzE,yEAAyE;AACzE,6EAA6E;AAC7E,2EAA2E;AAC3E,4BAA4B;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"clearResourcesPlan.js","sourceRoot":"","sources":["../../../src/lib/clearResourcesPlan.ts"],"names":[],"mappings":"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","sourcesContent":["// The question flow for `qpq clear-resources`: enumerate every service's OWNED\n// data resources (storage drives -> buckets, key value stores -> tables) from\n// its live qpq config, multi-select which to empty, then type the deploy\n// target back to confirm - emptying deletes stored data, so a y/n prompt\n// isn't enough friction. Owned-only so a cross-module ref (e.g. a membership\n// table every service declares against one owner) appears exactly once, on\n// the service that owns it.\nimport { qpqCoreUtils } from '@qpqjs/core';\n\nimport { getServiceNames } from './discovery';\nimport { getDeployTargetLabel } from './getDeployTargetLabel';\nimport { promptCheckbox, promptSelect, promptTypedConfirmation } from './prompts';\nimport { loadServiceQpqConfig } from './qpqConfigs';\nimport { createOutput } from './out';\n\nconst out = createOutput('clear-resources-plan');\n\nexport type ClearableResourceKind = 'storageDrive' | 'keyValueStore';\n\nexport type ClearableResource = {\n service: string;\n kind: ClearableResourceKind;\n // The config-level resource name (e.g. 'templateTestAssets', 'userTenantLinks');\n // the platform driver resolves it to the concrete runtime name (bucket/table).\n resourceName: string;\n};\n\nexport type ClearResourcesPlan =\n | { kind: 'cancelled' }\n // Empty (not destroy) the selected resources' stored data.\n | { kind: 'resources'; appName: string; resources: ClearableResource[] };\n\n// Every owned data resource across the app's services, in service order.\nconst getClearableResources = (appName: string): ClearableResource[] => {\n const resources: ClearableResource[] = [];\n\n for (const service of getServiceNames(appName)) {\n const qpqConfig = loadServiceQpqConfig(appName, service);\n\n for (const drive of qpqCoreUtils.getOwnedStorageDriveConfigs(qpqConfig)) {\n resources.push({ service, kind: 'storageDrive', resourceName: drive.storageDrive });\n }\n\n for (const store of qpqCoreUtils.getOwnedKeyValueStoreConfigs(qpqConfig)) {\n resources.push({ service, kind: 'keyValueStore', resourceName: store.keyValueStoreName });\n }\n }\n\n return resources;\n};\n\nconst BY_SERVICE = 'By service — empty everything the selected services own';\nconst BY_RESOURCE = 'By resource — pick individual buckets/tables';\n\n// Service mode: pick services, take every resource each one owns.\nconst pickByService = async (clearable: ClearableResource[]): Promise<ClearableResource[]> => {\n const services = [...new Set(clearable.map((resource) => resource.service))];\n\n const countFor = (service: string) => clearable.filter((resource) => resource.service === service).length;\n\n const picked = await promptCheckbox(\n 'Select services to EMPTY (every bucket/table the service owns)',\n services.map((service) => ({ name: `${service} (${countFor(service)} resources)`, value: service })),\n );\n\n return clearable.filter((resource) => picked.includes(resource.service));\n};\n\n// Resource mode: pick individual buckets/tables.\nconst pickByResource = (clearable: ClearableResource[]): Promise<ClearableResource[]> =>\n promptCheckbox(\n 'Select resources to EMPTY (all stored data will be deleted)',\n clearable.map((resource) => ({\n name: `${resource.service} — ${resource.resourceName} (${resource.kind === 'storageDrive' ? 'bucket' : 'table'})`,\n value: resource,\n })),\n );\n\nexport const promptClearResourcesPlan = async (appName: string): Promise<ClearResourcesPlan> => {\n const deployInfo = getDeployTargetLabel();\n\n const clearable = getClearableResources(appName);\n\n if (clearable.length === 0) {\n out.log('No storage drives or key value stores found in this app.');\n return { kind: 'cancelled' };\n }\n\n const mode = await promptSelect('How do you want to select what to empty?', [BY_SERVICE, BY_RESOURCE]);\n\n const resources = mode === BY_SERVICE ? await pickByService(clearable) : await pickByResource(clearable);\n\n if (resources.length === 0) {\n out.log('Nothing selected. Exiting!');\n return { kind: 'cancelled' };\n }\n\n out.log(`\\nThis will EMPTY (delete all stored data from) ${resources.length} resource(s) in [${deployInfo}]:`);\n for (const resource of resources) {\n out.log(` ${resource.service} — ${resource.resourceName} (${resource.kind === 'storageDrive' ? 'bucket' : 'table'})`);\n }\n\n if (!(await promptTypedConfirmation(deployInfo))) {\n return { kind: 'cancelled' };\n }\n\n return { kind: 'resources', appName, resources };\n};\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Nullable } from '@qpqjs/core';
|
|
1
2
|
export type StackType = 'all' | 'inf' | 'api' | 'web' | 'views';
|
|
2
3
|
export type DeployPlan = {
|
|
3
4
|
kind: 'cancelled';
|
|
@@ -14,7 +15,7 @@ export type DeployPlan = {
|
|
|
14
15
|
services: string[];
|
|
15
16
|
stacks: StackType;
|
|
16
17
|
};
|
|
17
|
-
export declare const buildDeployPlanFromArgs: (appName: string, argv: string[]) => DeployPlan
|
|
18
|
+
export declare const buildDeployPlanFromArgs: (appName: string, argv: string[]) => Nullable<DeployPlan>;
|
|
18
19
|
export declare const promptDeployPlan: (appName: string) => Promise<DeployPlan>;
|
|
19
20
|
export type TeardownPlan = {
|
|
20
21
|
kind: 'cancelled';
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
// The interactive question flow shared by `qpq go` (sequential AWS deploy) and
|
|
2
|
-
// a future dockerized variant: pick an app, confirm the target, pick services
|
|
3
|
-
// (or domain/bootstrap), pick stacks. buildDeployPlanFromArgs is the
|
|
4
|
-
// non-interactive shortcut (`qpq go all all`) that skips every prompt.
|
|
5
1
|
import { getPositionalArgs } from './args';
|
|
6
|
-
import { getDeployTargetLabel } from './getDeployTargetLabel';
|
|
7
2
|
import { getServiceNames } from './discovery';
|
|
8
|
-
import {
|
|
3
|
+
import { getDeployTargetLabel } from './getDeployTargetLabel';
|
|
4
|
+
import { promptSelect, promptTypedConfirmation, promptYesNo } from './prompts';
|
|
5
|
+
import { CliError } from './CliError';
|
|
6
|
+
import { createOutput } from './out';
|
|
7
|
+
const out = createOutput('deploy-prompts');
|
|
9
8
|
const STACK_TYPES = ['all', 'inf', 'api', 'web', 'views'];
|
|
10
9
|
// Non-interactive deploy plan from positional args: `qpq go <services> [stacks]`.
|
|
11
10
|
// services: 'all' | comma-separated service names | 'account' | 'domain' | 'bootstrap'
|
|
@@ -28,23 +27,26 @@ export const buildDeployPlanFromArgs = (appName, argv) => {
|
|
|
28
27
|
return { kind: 'bootstrap', appName, includeDomain: false };
|
|
29
28
|
}
|
|
30
29
|
const allServices = getServiceNames(appName);
|
|
31
|
-
const services = servicesArg === 'all'
|
|
30
|
+
const services = servicesArg === 'all'
|
|
31
|
+
? allServices
|
|
32
|
+
: servicesArg
|
|
33
|
+
.split(',')
|
|
34
|
+
.map((s) => s.trim())
|
|
35
|
+
.filter(Boolean);
|
|
32
36
|
const unknownServices = services.filter((service) => !allServices.includes(service));
|
|
33
37
|
if (unknownServices.length > 0) {
|
|
34
|
-
|
|
35
|
-
process.exit(1);
|
|
38
|
+
throw new CliError(`Unknown service(s): ${unknownServices.join(', ')}. Valid services: ${allServices.join(', ')}`);
|
|
36
39
|
}
|
|
37
40
|
const stacks = (stacksArg ?? 'all');
|
|
38
41
|
if (!STACK_TYPES.includes(stacks)) {
|
|
39
|
-
|
|
40
|
-
process.exit(1);
|
|
42
|
+
throw new CliError(`Unknown stacks '${stacksArg}'. Valid stacks: ${STACK_TYPES.join(', ')}`);
|
|
41
43
|
}
|
|
42
44
|
return { kind: 'services', appName, services: [...new Set(services)], stacks };
|
|
43
45
|
};
|
|
44
46
|
export const promptDeployPlan = async (appName) => {
|
|
45
47
|
const deployInfo = getDeployTargetLabel();
|
|
46
48
|
if (!(await promptYesNo(`Deploying to: [${deployInfo}]`))) {
|
|
47
|
-
|
|
49
|
+
out.log('Exiting!');
|
|
48
50
|
return { kind: 'cancelled' };
|
|
49
51
|
}
|
|
50
52
|
let services = [];
|
|
@@ -76,7 +78,7 @@ export const promptDeployPlan = async (appName) => {
|
|
|
76
78
|
return { kind: 'services', appName, services, stacks };
|
|
77
79
|
};
|
|
78
80
|
// The question flow for `qpq teardown`: pick services, then type the deploy
|
|
79
|
-
// target back to confirm
|
|
81
|
+
// target back to confirm, destroying inf stacks takes user data (key value
|
|
80
82
|
// stores, user directories) with it, so a y/n prompt isn't enough friction.
|
|
81
83
|
export const promptTeardownPlan = async (appName) => {
|
|
82
84
|
const deployInfo = getDeployTargetLabel();
|
|
@@ -92,10 +94,8 @@ export const promptTeardownPlan = async (appName) => {
|
|
|
92
94
|
services.push(service);
|
|
93
95
|
} while (await promptYesNo('Do you want to tear down another service?'));
|
|
94
96
|
services = [...new Set(services)];
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (confirmation !== deployInfo) {
|
|
98
|
-
console.log('Confirmation did not match. Exiting!');
|
|
97
|
+
out.log(`\nThis will DESTROY the web, api and inf stacks (including all stored data) for: ${services.join(', ')}`);
|
|
98
|
+
if (!(await promptTypedConfirmation(deployInfo))) {
|
|
99
99
|
return { kind: 'cancelled' };
|
|
100
100
|
}
|
|
101
101
|
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"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { getDevServerDistPath } from '@qpqjs/core';
|
|
2
|
+
import { USER_SCRIPT_CHILD_BUNDLE_NAME } from '@qpqjs/deploy-rspack';
|
|
1
3
|
import fs from 'fs';
|
|
2
4
|
import path from 'path';
|
|
3
|
-
import { USER_SCRIPT_CHILD_BUNDLE_NAME } from '@qpqjs/deploy-rspack';
|
|
4
5
|
import { DEFAULT_DEV_SERVER_PORTS, getDevServerConfigPath } from './devServerPorts';
|
|
5
6
|
/** Env vars the docker image sets so the running server knows how the outside world reaches it. */
|
|
6
7
|
export const DEV_SERVER_PUBLIC_HOST_ENV = 'QPQ_PUBLIC_HOST';
|
|
@@ -43,7 +44,7 @@ const getRunCall = (mode) => {
|
|
|
43
44
|
* shares one bundle shape, so a local migration run rehearses exactly the deployed path.
|
|
44
45
|
*/
|
|
45
46
|
export const writeDevServerEntry = (root, appName, mode = 'serve') => {
|
|
46
|
-
const entryDir =
|
|
47
|
+
const entryDir = getDevServerDistPath(root);
|
|
47
48
|
const entryPath = path.join(entryDir, ENTRY_FILE_NAMES[mode]);
|
|
48
49
|
const overridesPath = getDevServerConfigPath(appName);
|
|
49
50
|
const overridesImport = fs.existsSync(overridesPath)
|
|
@@ -98,11 +99,11 @@ ${getRunCall(mode)}
|
|
|
98
99
|
* serves that one call with the service's config and module loader, then exits.
|
|
99
100
|
*/
|
|
100
101
|
export const writeUserScriptChildEntry = (root) => {
|
|
101
|
-
const entryDir =
|
|
102
|
+
const entryDir = getDevServerDistPath(root);
|
|
102
103
|
const entryPath = path.join(entryDir, USER_SCRIPT_CHILD_ENTRY_FILE_NAME);
|
|
103
104
|
fs.mkdirSync(entryDir, { recursive: true });
|
|
104
105
|
fs.writeFileSync(entryPath, `// GENERATED by qpq — do not edit.
|
|
105
|
-
import { qpqCoreUtils } from '@qpqjs/core';
|
|
106
|
+
import { getDevServerDistPath, qpqCoreUtils } from '@qpqjs/core';
|
|
106
107
|
import { runUserScriptChildMain } from '@qpqjs/actionprocessor-node';
|
|
107
108
|
import {
|
|
108
109
|
qpqConfigs,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devServerEntry.js","sourceRoot":"","sources":["../../../src/lib/devServerEntry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,
|
|
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"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { requireModuleDefault } from '@qpqjs/deploy-aws';
|
|
1
2
|
import fs from 'fs';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import { getAppDirectory } from './discovery';
|
|
@@ -19,9 +20,7 @@ export const readDevServerPorts = (appName) => {
|
|
|
19
20
|
if (!fs.existsSync(configPath)) {
|
|
20
21
|
return DEFAULT_DEV_SERVER_PORTS;
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
-
const loaded = require(configPath.replace(/\.ts$/, ''));
|
|
24
|
-
const overrides = (loaded.default ?? loaded);
|
|
23
|
+
const overrides = requireModuleDefault(configPath.replace(/\.ts$/, ''));
|
|
25
24
|
return { ...DEFAULT_DEV_SERVER_PORTS, ...overrides.ports };
|
|
26
25
|
};
|
|
27
26
|
//# sourceMappingURL=devServerPorts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devServerPorts.js","sourceRoot":"","sources":["../../../src/lib/devServerPorts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"devServerPorts.js","sourceRoot":"","sources":["../../../src/lib/devServerPorts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAK9C,wHAAwH;AACxH,MAAM,CAAC,MAAM,wBAAwB,GAA2B;IAC9D,GAAG,EAAE,IAAI;IACT,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,IAAI;CAClB,CAAC;AAEF,gGAAgG;AAChG,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,qBAAqB,CAAC,CAAC;AAE9H;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,OAAe,EAA0B,EAAE;IAC5E,MAAM,UAAU,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;IACnD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,wBAAwB,CAAC;IAClC,CAAC;IAED,MAAM,SAAS,GAAG,oBAAoB,CAA2B,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAElG,OAAO,EAAE,GAAG,wBAAwB,EAAE,GAAG,SAAS,CAAC,KAAK,EAAE,CAAC;AAC7D,CAAC,CAAC","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[];
|
package/lib/esm/lib/discovery.js
CHANGED
|
@@ -1,40 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
// scripts set cwd there).
|
|
1
|
+
import { getAppPath, getAppsPath, getAvailableAppNames, getBackendServiceNames, getServiceNamesWithSubdir as getServiceNamesWithSubdirIn, getServiceNamesWithViews as getServiceNamesWithViewsIn, getServicePath, } from '@qpqjs/deploy-rspack';
|
|
2
|
+
// The consumer repo root, qpq always runs from the workspace root (npm
|
|
3
|
+
// scripts set cwd there). Discovery itself is deploy-rspack's; these bind it to that root.
|
|
5
4
|
export const getRoot = () => process.cwd();
|
|
6
|
-
export const getAppsDirectory = () =>
|
|
7
|
-
export const getAppDirectory = (appName) =>
|
|
8
|
-
export const getServiceDirectory = (appName, service) =>
|
|
9
|
-
|
|
10
|
-
export const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return [];
|
|
14
|
-
}
|
|
15
|
-
return fs.readdirSync(appsPath).filter((name) => {
|
|
16
|
-
const servicesPath = path.join(getAppDirectory(name), 'services');
|
|
17
|
-
return fs.statSync(getAppDirectory(name)).isDirectory() && fs.existsSync(servicesPath) && fs.statSync(servicesPath).isDirectory();
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
// Every service directory under an app's services/ folder (no subdir filter).
|
|
21
|
-
export const getAllServiceNames = (appName) => {
|
|
22
|
-
const directoryPath = path.join(getAppDirectory(appName), 'services');
|
|
23
|
-
if (!fs.existsSync(directoryPath)) {
|
|
24
|
-
return [];
|
|
25
|
-
}
|
|
26
|
-
return fs.readdirSync(directoryPath).filter((name) => fs.statSync(getServiceDirectory(appName, name)).isDirectory());
|
|
27
|
-
};
|
|
28
|
-
// True when a service contains the given child directory (e.g. 'service' for a
|
|
29
|
-
// backend, 'views' for a frontend).
|
|
30
|
-
export const serviceHasSubdir = (appName, service, subdir) => {
|
|
31
|
-
const subdirPath = path.join(getServiceDirectory(appName, service), subdir);
|
|
32
|
-
return fs.existsSync(subdirPath) && fs.statSync(subdirPath).isDirectory();
|
|
33
|
-
};
|
|
34
|
-
// Services that contain a given child directory.
|
|
35
|
-
export const getServiceNamesWithSubdir = (appName, subdir) => getAllServiceNames(appName).filter((service) => serviceHasSubdir(appName, service, subdir));
|
|
36
|
-
// Backend services — those with a service/ subdir.
|
|
37
|
-
export const getServiceNames = (appName) => getServiceNamesWithSubdir(appName, 'service');
|
|
38
|
-
// Backend services that also ship a frontend (views/ subdir).
|
|
39
|
-
export const getServiceNamesWithViews = (appName) => getServiceNames(appName).filter((service) => serviceHasSubdir(appName, service, 'views'));
|
|
5
|
+
export const getAppsDirectory = () => getAppsPath(getRoot());
|
|
6
|
+
export const getAppDirectory = (appName) => getAppPath(getRoot(), appName);
|
|
7
|
+
export const getServiceDirectory = (appName, service) => getServicePath(getRoot(), appName, service);
|
|
8
|
+
export const getAvailableApps = () => getAvailableAppNames(getRoot());
|
|
9
|
+
export const getServiceNamesWithSubdir = (appName, subdir) => getServiceNamesWithSubdirIn(getRoot(), appName, subdir);
|
|
10
|
+
export const getServiceNames = (appName) => getBackendServiceNames(getRoot(), appName);
|
|
11
|
+
export const getServiceNamesWithViews = (appName) => getServiceNamesWithViewsIn(getRoot(), appName);
|
|
40
12
|
//# sourceMappingURL=discovery.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../../src/lib/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../../src/lib/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,WAAW,EACX,oBAAoB,EACpB,sBAAsB,EACtB,yBAAyB,IAAI,2BAA2B,EACxD,wBAAwB,IAAI,0BAA0B,EACtD,cAAc,GACf,MAAM,sBAAsB,CAAC;AAE9B,uEAAuE;AACvE,2FAA2F;AAC3F,MAAM,CAAC,MAAM,OAAO,GAAG,GAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;AAEnD,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAW,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;AACrE,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AAC3F,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,OAAe,EAAE,OAAe,EAAU,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAE7H,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAa,EAAE,CAAC,oBAAoB,CAAC,OAAO,EAAE,CAAC,CAAC;AAEhF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,OAAe,EAAE,MAAc,EAAY,EAAE,CAAC,2BAA2B,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAEhJ,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAe,EAAY,EAAE,CAAC,sBAAsB,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AAEzG,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,OAAe,EAAY,EAAE,CAAC,0BAA0B,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC","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"]}
|