quidproquo-deploy-awscdk 0.1.26 → 0.1.28

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 (50) hide show
  1. package/lib/commonjs/appWorkspace/QpqAppDeployContext.d.ts +16 -0
  2. package/lib/commonjs/appWorkspace/QpqAppDeployContext.js +3 -0
  3. package/lib/commonjs/appWorkspace/QpqAppDeployContext.js.map +1 -0
  4. package/lib/commonjs/appWorkspace/createWorkspaceQpqCdkApp.d.ts +0 -1
  5. package/lib/commonjs/appWorkspace/createWorkspaceQpqCdkApp.js +22 -36
  6. package/lib/commonjs/appWorkspace/createWorkspaceQpqCdkApp.js.map +1 -1
  7. package/lib/commonjs/appWorkspace/getQpqAppDeployContext.d.ts +3 -0
  8. package/lib/commonjs/appWorkspace/getQpqAppDeployContext.js +26 -0
  9. package/lib/commonjs/appWorkspace/getQpqAppDeployContext.js.map +1 -0
  10. package/lib/commonjs/appWorkspace/getWorkspaceQpqConfigs.d.ts +1 -1
  11. package/lib/commonjs/appWorkspace/getWorkspaceQpqConfigs.js +3 -3
  12. package/lib/commonjs/appWorkspace/getWorkspaceQpqConfigs.js.map +1 -1
  13. package/lib/commonjs/appWorkspace/index.d.ts +2 -1
  14. package/lib/commonjs/appWorkspace/index.js +2 -1
  15. package/lib/commonjs/appWorkspace/index.js.map +1 -1
  16. package/lib/commonjs/constructs/feature/core/keyValueStore/QpqCoreKeyValueStoreConstruct.js +4 -3
  17. package/lib/commonjs/constructs/feature/core/keyValueStore/QpqCoreKeyValueStoreConstruct.js.map +1 -1
  18. package/lib/esm/appWorkspace/QpqAppDeployContext.d.ts +16 -0
  19. package/lib/esm/appWorkspace/QpqAppDeployContext.js +2 -0
  20. package/lib/esm/appWorkspace/QpqAppDeployContext.js.map +1 -0
  21. package/lib/esm/appWorkspace/createWorkspaceQpqCdkApp.d.ts +0 -1
  22. package/lib/esm/appWorkspace/createWorkspaceQpqCdkApp.js +21 -31
  23. package/lib/esm/appWorkspace/createWorkspaceQpqCdkApp.js.map +1 -1
  24. package/lib/esm/appWorkspace/getQpqAppDeployContext.d.ts +3 -0
  25. package/lib/esm/appWorkspace/getQpqAppDeployContext.js +18 -0
  26. package/lib/esm/appWorkspace/getQpqAppDeployContext.js.map +1 -0
  27. package/lib/esm/appWorkspace/getWorkspaceQpqConfigs.d.ts +1 -1
  28. package/lib/esm/appWorkspace/getWorkspaceQpqConfigs.js +3 -3
  29. package/lib/esm/appWorkspace/getWorkspaceQpqConfigs.js.map +1 -1
  30. package/lib/esm/appWorkspace/index.d.ts +2 -1
  31. package/lib/esm/appWorkspace/index.js +2 -1
  32. package/lib/esm/appWorkspace/index.js.map +1 -1
  33. package/lib/esm/constructs/feature/core/keyValueStore/QpqCoreKeyValueStoreConstruct.js +4 -3
  34. package/lib/esm/constructs/feature/core/keyValueStore/QpqCoreKeyValueStoreConstruct.js.map +1 -1
  35. package/package.json +7 -7
  36. package/src/appWorkspace/QpqAppDeployContext.ts +20 -0
  37. package/src/appWorkspace/createWorkspaceQpqCdkApp.ts +22 -31
  38. package/src/appWorkspace/getQpqAppDeployContext.test.ts +51 -0
  39. package/src/appWorkspace/getQpqAppDeployContext.ts +25 -0
  40. package/src/appWorkspace/getWorkspaceQpqConfigs.ts +4 -4
  41. package/src/appWorkspace/index.ts +2 -1
  42. package/src/constructs/feature/core/keyValueStore/QpqCoreKeyValueStoreConstruct.test.ts +89 -0
  43. package/src/constructs/feature/core/keyValueStore/QpqCoreKeyValueStoreConstruct.ts +4 -3
  44. package/lib/commonjs/appWorkspace/qpqAppDeployConfig.d.ts +0 -20
  45. package/lib/commonjs/appWorkspace/qpqAppDeployConfig.js +0 -37
  46. package/lib/commonjs/appWorkspace/qpqAppDeployConfig.js.map +0 -1
  47. package/lib/esm/appWorkspace/qpqAppDeployConfig.d.ts +0 -20
  48. package/lib/esm/appWorkspace/qpqAppDeployConfig.js +0 -28
  49. package/lib/esm/appWorkspace/qpqAppDeployConfig.js.map +0 -1
  50. package/src/appWorkspace/qpqAppDeployConfig.ts +0 -82
@@ -0,0 +1,16 @@
1
+ import { QpqAppDeploySettings } from 'quidproquo-core';
2
+ /**
3
+ * Deploy-time context handed to the app's account.qpq.ts / bootstrap.qpq.ts config
4
+ * fragments. Only AWS deployments reach the CDK app, so the AWS identity is required.
5
+ */
6
+ export type QpqAppDeployContext = {
7
+ appName: string;
8
+ appDir: string;
9
+ deploymentName: string;
10
+ name: string;
11
+ environment: string;
12
+ feature?: string;
13
+ settings: QpqAppDeploySettings;
14
+ accountId: string;
15
+ region: string;
16
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=QpqAppDeployContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QpqAppDeployContext.js","sourceRoot":"","sources":["../../../src/appWorkspace/QpqAppDeployContext.ts"],"names":[],"mappings":"","sourcesContent":["import { QpqAppDeploySettings } from 'quidproquo-core';\n\n/**\n * Deploy-time context handed to the app's account.qpq.ts / bootstrap.qpq.ts config\n * fragments. Only AWS deployments reach the CDK app, so the AWS identity is required.\n */\nexport type QpqAppDeployContext = {\n appName: string;\n appDir: string;\n\n deploymentName: string;\n\n name: string;\n environment: string;\n feature?: string;\n settings: QpqAppDeploySettings;\n\n accountId: string;\n region: string;\n};\n"]}
@@ -1,3 +1,2 @@
1
1
  import * as cdk from 'aws-cdk-lib';
2
- export declare const findWorkspaceRoot: (startDir?: string) => string;
3
2
  export declare const createWorkspaceQpqCdkApp: () => cdk.App;
@@ -32,54 +32,40 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
35
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.createWorkspaceQpqCdkApp = exports.findWorkspaceRoot = void 0;
40
- const fs_1 = __importDefault(require("fs"));
41
- const path_1 = __importDefault(require("path"));
36
+ exports.createWorkspaceQpqCdkApp = void 0;
37
+ const quidproquo_config_aws_1 = require("quidproquo-config-aws");
38
+ const quidproquo_core_1 = require("quidproquo-core");
42
39
  const QPQApp_1 = require("../QPQApp");
43
40
  const stacks_1 = require("../stacks");
44
41
  const qpqDeployAwsCdkUtils = __importStar(require("../utils"));
42
+ const getQpqAppDeployContext_1 = require("./getQpqAppDeployContext");
45
43
  const getWorkspaceQpqConfigs_1 = require("./getWorkspaceQpqConfigs");
46
- const qpqAppDeployConfig_1 = require("./qpqAppDeployConfig");
47
- // Walk up from cwd to the workspace root (the directory with an apps/ folder).
48
- const findWorkspaceRoot = (startDir = process.cwd()) => {
49
- let dir = startDir;
50
- while (!fs_1.default.existsSync(path_1.default.join(dir, 'apps')) || !fs_1.default.existsSync(path_1.default.join(dir, 'package.json'))) {
51
- const parent = path_1.default.dirname(dir);
52
- if (parent === dir)
53
- throw new Error('Could not find workspace root (a directory containing apps/ and package.json)');
54
- dir = parent;
55
- }
56
- return dir;
57
- };
58
- exports.findWorkspaceRoot = findWorkspaceRoot;
59
- // The generic CDK app for a QPQ app workspace — the same stacks for every
44
+ // The generic CDK app for a QPQ app workspace: the same stacks for every
60
45
  // product; everything app-specific comes from apps/<app>/ (deploy.config.json
61
46
  // + optional account.qpq.ts / bootstrap.qpq.ts fragments).
62
47
  //
63
- // Driven by environment variables set by the invoking tool (`qpq go`):
64
- // DEPLOY_APP_NAME (required) app folder under apps/
65
- // DEPLOY_SERVICE_NAME (optional) also synth the service's inf/web/api stacks
66
- // ENVIRONMENT (required) environment name in deploy.config.json
67
- // ACTOR_NAME (optional) actor/feature deploys
68
- // AWS_DEFAULT_ACCOUNT / AWS_DEFAULT_REGION (optional) identity override
48
+ // Driven by two env vars set by the invoking tool (`qpq go`):
49
+ // DEPLOY_APP_NAME app folder under apps/
50
+ // DEPLOY_NAME deployment name in that app's deploy.config.json
51
+ // plus DEPLOY_SERVICE_NAME (optional) to also synth a service's inf/web/api
52
+ // stacks. Everything else (identity, environment, feature, settings) comes from
53
+ // the deployment entry, primed here before any service config is required.
69
54
  const createWorkspaceQpqCdkApp = () => {
70
- const root = (0, exports.findWorkspaceRoot)();
71
- const deployAppName = process.env.DEPLOY_APP_NAME;
55
+ // qpq spawns cdk with cwd at the workspace root.
56
+ const root = process.cwd();
57
+ const deployAppName = process.env[quidproquo_core_1.QpqDeployEnvVar.deployAppName];
72
58
  if (!deployAppName) {
73
- throw new Error('DEPLOY_APP_NAME environment variable is not set.');
59
+ throw new Error(`${quidproquo_core_1.QpqDeployEnvVar.deployAppName} environment variable is not set.`);
74
60
  }
75
- const environment = process.env.ENVIRONMENT;
76
- if (!environment) {
77
- throw new Error('ENVIRONMENT environment variable is not set.');
61
+ const deploymentName = process.env[quidproquo_core_1.QpqDeployEnvVar.deployName];
62
+ if (!deploymentName) {
63
+ throw new Error(`${quidproquo_core_1.QpqDeployEnvVar.deployName} environment variable is not set.`);
78
64
  }
79
- const ctx = (0, qpqAppDeployConfig_1.getQpqAppDeployContext)(root, deployAppName, environment, process.env.ACTOR_NAME, {
80
- accountId: process.env.AWS_DEFAULT_ACCOUNT,
81
- region: process.env.AWS_DEFAULT_REGION,
82
- });
65
+ const deployment = (0, quidproquo_config_aws_1.validateAwsDeployment)(deploymentName, (0, quidproquo_config_aws_1.getQpqAppDeployment)(root, deployAppName, deploymentName));
66
+ (0, quidproquo_config_aws_1.primeDeploymentEnv)(deployAppName, deploymentName, deployment);
67
+ (0, quidproquo_config_aws_1.primeAwsDeploymentEnv)(deployment);
68
+ const ctx = (0, getQpqAppDeployContext_1.getQpqAppDeployContext)(root, deployAppName, deploymentName);
83
69
  const app = (0, QPQApp_1.createQPQApp)();
84
70
  // Account-level guardrails - one statically named stack per AWS account,
85
71
  // independent of any app. Exactly one repo/config owns this per account, and
@@ -1 +1 @@
1
- {"version":3,"file":"createWorkspaceQpqCdkApp.js","sourceRoot":"","sources":["../../../src/appWorkspace/createWorkspaceQpqCdkApp.ts"],"names":[],"mappings":"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","sourcesContent":["import * as cdk from 'aws-cdk-lib';\nimport fs from 'fs';\nimport path from 'path';\n\nimport { createQPQApp } from '../QPQApp';\nimport {\n AccountQpqStack,\n ApiQpqServiceStack,\n BootstrapQpqServiceStack,\n DomainQpqServiceStack,\n InfQpqServiceStack,\n WebQpqServiceStack,\n} from '../stacks';\nimport * as qpqDeployAwsCdkUtils from '../utils';\nimport { getWorkspaceAccountQpqConfig, getWorkspaceBootstrapQpqConfig, getWorkspaceServiceQpqConfig } from './getWorkspaceQpqConfigs';\nimport { getQpqAppDeployContext } from './qpqAppDeployConfig';\n\n// Walk up from cwd to the workspace root (the directory with an apps/ folder).\nexport const findWorkspaceRoot = (startDir: string = process.cwd()): string => {\n let dir = startDir;\n while (!fs.existsSync(path.join(dir, 'apps')) || !fs.existsSync(path.join(dir, 'package.json'))) {\n const parent = path.dirname(dir);\n if (parent === dir) throw new Error('Could not find workspace root (a directory containing apps/ and package.json)');\n dir = parent;\n }\n return dir;\n};\n\n// The generic CDK app for a QPQ app workspace — the same stacks for every\n// product; everything app-specific comes from apps/<app>/ (deploy.config.json\n// + optional account.qpq.ts / bootstrap.qpq.ts fragments).\n//\n// Driven by environment variables set by the invoking tool (`qpq go`):\n// DEPLOY_APP_NAME (required) app folder under apps/\n// DEPLOY_SERVICE_NAME (optional) also synth the service's inf/web/api stacks\n// ENVIRONMENT (required) environment name in deploy.config.json\n// ACTOR_NAME (optional) actor/feature deploys\n// AWS_DEFAULT_ACCOUNT / AWS_DEFAULT_REGION (optional) identity override\nexport const createWorkspaceQpqCdkApp = (): cdk.App => {\n const root = findWorkspaceRoot();\n\n const deployAppName = process.env.DEPLOY_APP_NAME;\n if (!deployAppName) {\n throw new Error('DEPLOY_APP_NAME environment variable is not set.');\n }\n\n const environment = process.env.ENVIRONMENT;\n if (!environment) {\n throw new Error('ENVIRONMENT environment variable is not set.');\n }\n\n const ctx = getQpqAppDeployContext(root, deployAppName, environment, process.env.ACTOR_NAME, {\n accountId: process.env.AWS_DEFAULT_ACCOUNT,\n region: process.env.AWS_DEFAULT_REGION,\n });\n\n const app = createQPQApp();\n\n // Account-level guardrails - one statically named stack per AWS account,\n // independent of any app. Exactly one repo/config owns this per account, and\n // actor deploys must not deploy it.\n new AccountQpqStack(app, qpqDeployAwsCdkUtils.getAccountStackName(), {\n qpqConfig: getWorkspaceAccountQpqConfig(ctx),\n });\n\n // Bootstrapping stacks — deploy when their config changes.\n const bootstrapQpqConfig = getWorkspaceBootstrapQpqConfig(ctx);\n\n new DomainQpqServiceStack(app, qpqDeployAwsCdkUtils.getDomainStackName(bootstrapQpqConfig), {\n qpqConfig: bootstrapQpqConfig,\n });\n\n new BootstrapQpqServiceStack(app, qpqDeployAwsCdkUtils.getBootstrapStackName(bootstrapQpqConfig), {\n qpqConfig: bootstrapQpqConfig,\n });\n\n if (process.env.DEPLOY_SERVICE_NAME) {\n const qpqConfig = getWorkspaceServiceQpqConfig(root, deployAppName, process.env.DEPLOY_SERVICE_NAME);\n\n new InfQpqServiceStack(app, qpqDeployAwsCdkUtils.getInfStackName(qpqConfig), {\n qpqConfig,\n });\n new WebQpqServiceStack(app, qpqDeployAwsCdkUtils.getWebStackName(qpqConfig), {\n qpqConfig,\n });\n new ApiQpqServiceStack(app, qpqDeployAwsCdkUtils.getApiStackName(qpqConfig), {\n qpqConfig,\n });\n }\n\n return app;\n};\n"]}
1
+ {"version":3,"file":"createWorkspaceQpqCdkApp.js","sourceRoot":"","sources":["../../../src/appWorkspace/createWorkspaceQpqCdkApp.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iEAA8H;AAC9H,qDAAkD;AAIlD,sCAAyC;AACzC,sCAOmB;AACnB,+DAAiD;AACjD,qEAAkE;AAClE,qEAAsI;AAEtI,yEAAyE;AACzE,8EAA8E;AAC9E,2DAA2D;AAC3D,EAAE;AACF,8DAA8D;AAC9D,gDAAgD;AAChD,0EAA0E;AAC1E,4EAA4E;AAC5E,gFAAgF;AAChF,2EAA2E;AACpE,MAAM,wBAAwB,GAAG,GAAY,EAAE;IACpD,iDAAiD;IACjD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAE3B,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,iCAAe,CAAC,aAAa,CAAC,CAAC;IACjE,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,GAAG,iCAAe,CAAC,aAAa,mCAAmC,CAAC,CAAC;IACvF,CAAC;IAED,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,iCAAe,CAAC,UAAU,CAAC,CAAC;IAC/D,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,GAAG,iCAAe,CAAC,UAAU,mCAAmC,CAAC,CAAC;IACpF,CAAC;IAED,MAAM,UAAU,GAAG,IAAA,6CAAqB,EAAC,cAAc,EAAE,IAAA,2CAAmB,EAAC,IAAI,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC;IACnH,IAAA,0CAAkB,EAAC,aAAa,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC;IAC9D,IAAA,6CAAqB,EAAC,UAAU,CAAC,CAAC;IAClC,MAAM,GAAG,GAAG,IAAA,+CAAsB,EAAC,IAAI,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC;IAExE,MAAM,GAAG,GAAG,IAAA,qBAAY,GAAE,CAAC;IAE3B,yEAAyE;IACzE,6EAA6E;IAC7E,oCAAoC;IACpC,IAAI,wBAAe,CAAC,GAAG,EAAE,oBAAoB,CAAC,mBAAmB,EAAE,EAAE;QACnE,SAAS,EAAE,IAAA,qDAA4B,EAAC,GAAG,CAAC;KAC7C,CAAC,CAAC;IAEH,2DAA2D;IAC3D,MAAM,kBAAkB,GAAG,IAAA,uDAA8B,EAAC,GAAG,CAAC,CAAC;IAE/D,IAAI,8BAAqB,CAAC,GAAG,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,EAAE;QAC1F,SAAS,EAAE,kBAAkB;KAC9B,CAAC,CAAC;IAEH,IAAI,iCAAwB,CAAC,GAAG,EAAE,oBAAoB,CAAC,qBAAqB,CAAC,kBAAkB,CAAC,EAAE;QAChG,SAAS,EAAE,kBAAkB;KAC9B,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,IAAA,qDAA4B,EAAC,IAAI,EAAE,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QAErG,IAAI,2BAAkB,CAAC,GAAG,EAAE,oBAAoB,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE;YAC3E,SAAS;SACV,CAAC,CAAC;QACH,IAAI,2BAAkB,CAAC,GAAG,EAAE,oBAAoB,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE;YAC3E,SAAS;SACV,CAAC,CAAC;QACH,IAAI,2BAAkB,CAAC,GAAG,EAAE,oBAAoB,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE;YAC3E,SAAS;SACV,CAAC,CAAC;IACL,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAtDW,QAAA,wBAAwB,4BAsDnC","sourcesContent":["import { getQpqAppDeployment, primeAwsDeploymentEnv, primeDeploymentEnv, validateAwsDeployment } from 'quidproquo-config-aws';\nimport { QpqDeployEnvVar } from 'quidproquo-core';\n\nimport * as cdk from 'aws-cdk-lib';\n\nimport { createQPQApp } from '../QPQApp';\nimport {\n AccountQpqStack,\n ApiQpqServiceStack,\n BootstrapQpqServiceStack,\n DomainQpqServiceStack,\n InfQpqServiceStack,\n WebQpqServiceStack,\n} from '../stacks';\nimport * as qpqDeployAwsCdkUtils from '../utils';\nimport { getQpqAppDeployContext } from './getQpqAppDeployContext';\nimport { getWorkspaceAccountQpqConfig, getWorkspaceBootstrapQpqConfig, getWorkspaceServiceQpqConfig } from './getWorkspaceQpqConfigs';\n\n// The generic CDK app for a QPQ app workspace: the same stacks for every\n// product; everything app-specific comes from apps/<app>/ (deploy.config.json\n// + optional account.qpq.ts / bootstrap.qpq.ts fragments).\n//\n// Driven by two env vars set by the invoking tool (`qpq go`):\n// DEPLOY_APP_NAME app folder under apps/\n// DEPLOY_NAME deployment name in that app's deploy.config.json\n// plus DEPLOY_SERVICE_NAME (optional) to also synth a service's inf/web/api\n// stacks. Everything else (identity, environment, feature, settings) comes from\n// the deployment entry, primed here before any service config is required.\nexport const createWorkspaceQpqCdkApp = (): cdk.App => {\n // qpq spawns cdk with cwd at the workspace root.\n const root = process.cwd();\n\n const deployAppName = process.env[QpqDeployEnvVar.deployAppName];\n if (!deployAppName) {\n throw new Error(`${QpqDeployEnvVar.deployAppName} environment variable is not set.`);\n }\n\n const deploymentName = process.env[QpqDeployEnvVar.deployName];\n if (!deploymentName) {\n throw new Error(`${QpqDeployEnvVar.deployName} environment variable is not set.`);\n }\n\n const deployment = validateAwsDeployment(deploymentName, getQpqAppDeployment(root, deployAppName, deploymentName));\n primeDeploymentEnv(deployAppName, deploymentName, deployment);\n primeAwsDeploymentEnv(deployment);\n const ctx = getQpqAppDeployContext(root, deployAppName, deploymentName);\n\n const app = createQPQApp();\n\n // Account-level guardrails - one statically named stack per AWS account,\n // independent of any app. Exactly one repo/config owns this per account, and\n // actor deploys must not deploy it.\n new AccountQpqStack(app, qpqDeployAwsCdkUtils.getAccountStackName(), {\n qpqConfig: getWorkspaceAccountQpqConfig(ctx),\n });\n\n // Bootstrapping stacks — deploy when their config changes.\n const bootstrapQpqConfig = getWorkspaceBootstrapQpqConfig(ctx);\n\n new DomainQpqServiceStack(app, qpqDeployAwsCdkUtils.getDomainStackName(bootstrapQpqConfig), {\n qpqConfig: bootstrapQpqConfig,\n });\n\n new BootstrapQpqServiceStack(app, qpqDeployAwsCdkUtils.getBootstrapStackName(bootstrapQpqConfig), {\n qpqConfig: bootstrapQpqConfig,\n });\n\n if (process.env.DEPLOY_SERVICE_NAME) {\n const qpqConfig = getWorkspaceServiceQpqConfig(root, deployAppName, process.env.DEPLOY_SERVICE_NAME);\n\n new InfQpqServiceStack(app, qpqDeployAwsCdkUtils.getInfStackName(qpqConfig), {\n qpqConfig,\n });\n new WebQpqServiceStack(app, qpqDeployAwsCdkUtils.getWebStackName(qpqConfig), {\n qpqConfig,\n });\n new ApiQpqServiceStack(app, qpqDeployAwsCdkUtils.getApiStackName(qpqConfig), {\n qpqConfig,\n });\n }\n\n return app;\n};\n"]}
@@ -0,0 +1,3 @@
1
+ import { QpqAppDeployContext } from './QpqAppDeployContext';
2
+ /** Builds the context the CDK app hands to config fragments. Throws for a non-AWS deployment, which never reaches CDK. */
3
+ export declare const getQpqAppDeployContext: (root: string, appName: string, deploymentName: string) => QpqAppDeployContext;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getQpqAppDeployContext = void 0;
7
+ const quidproquo_config_aws_1 = require("quidproquo-config-aws");
8
+ const path_1 = __importDefault(require("path"));
9
+ /** Builds the context the CDK app hands to config fragments. Throws for a non-AWS deployment, which never reaches CDK. */
10
+ const getQpqAppDeployContext = (root, appName, deploymentName) => {
11
+ var _a;
12
+ const deployment = (0, quidproquo_config_aws_1.validateAwsDeployment)(deploymentName, (0, quidproquo_config_aws_1.getQpqAppDeployment)(root, appName, deploymentName));
13
+ return {
14
+ appName,
15
+ appDir: path_1.default.join(root, 'apps', appName),
16
+ deploymentName,
17
+ name: deployment.name,
18
+ environment: deployment.environment,
19
+ feature: deployment.feature,
20
+ settings: (_a = deployment.settings) !== null && _a !== void 0 ? _a : {},
21
+ accountId: deployment.platformSettings.accountId,
22
+ region: deployment.platformSettings.region,
23
+ };
24
+ };
25
+ exports.getQpqAppDeployContext = getQpqAppDeployContext;
26
+ //# sourceMappingURL=getQpqAppDeployContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getQpqAppDeployContext.js","sourceRoot":"","sources":["../../../src/appWorkspace/getQpqAppDeployContext.ts"],"names":[],"mappings":";;;;;;AAAA,iEAAmF;AAEnF,gDAAwB;AAIxB,0HAA0H;AACnH,MAAM,sBAAsB,GAAG,CAAC,IAAY,EAAE,OAAe,EAAE,cAAsB,EAAuB,EAAE;;IACnH,MAAM,UAAU,GAAG,IAAA,6CAAqB,EAAC,cAAc,EAAE,IAAA,2CAAmB,EAAC,IAAI,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;IAE7G,OAAO;QACL,OAAO;QACP,MAAM,EAAE,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;QAExC,cAAc;QAEd,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,QAAQ,EAAE,MAAA,UAAU,CAAC,QAAQ,mCAAI,EAAE;QAEnC,SAAS,EAAE,UAAU,CAAC,gBAAgB,CAAC,SAAS;QAChD,MAAM,EAAE,UAAU,CAAC,gBAAgB,CAAC,MAAM;KACb,CAAC;AAClC,CAAC,CAAC;AAjBW,QAAA,sBAAsB,0BAiBjC","sourcesContent":["import { getQpqAppDeployment, validateAwsDeployment } from 'quidproquo-config-aws';\n\nimport path from 'path';\n\nimport { QpqAppDeployContext } from './QpqAppDeployContext';\n\n/** Builds the context the CDK app hands to config fragments. Throws for a non-AWS deployment, which never reaches CDK. */\nexport const getQpqAppDeployContext = (root: string, appName: string, deploymentName: string): QpqAppDeployContext => {\n const deployment = validateAwsDeployment(deploymentName, getQpqAppDeployment(root, appName, deploymentName));\n\n return {\n appName,\n appDir: path.join(root, 'apps', appName),\n\n deploymentName,\n\n name: deployment.name,\n environment: deployment.environment,\n feature: deployment.feature,\n settings: deployment.settings ?? {},\n\n accountId: deployment.platformSettings.accountId,\n region: deployment.platformSettings.region,\n } satisfies QpqAppDeployContext;\n};\n"]}
@@ -1,5 +1,5 @@
1
1
  import { QPQConfig } from 'quidproquo-core';
2
- import { QpqAppDeployContext } from './qpqAppDeployConfig';
2
+ import { QpqAppDeployContext } from './QpqAppDeployContext';
3
3
  export declare const getWorkspaceAccountQpqConfig: (ctx: QpqAppDeployContext) => QPQConfig;
4
4
  export declare const getWorkspaceBootstrapQpqConfig: (ctx: QpqAppDeployContext) => QPQConfig;
5
5
  export declare const getWorkspaceServiceQpqConfig: (root: string, appName: string, serviceName: string) => QPQConfig;
@@ -35,10 +35,10 @@ const getWorkspaceAccountQpqConfig = (ctx) => [
35
35
  exports.getWorkspaceAccountQpqConfig = getWorkspaceAccountQpqConfig;
36
36
  // The app's bootstrap config: apex/zone/cert/WAF-level resources every service
37
37
  // in the app builds on. The fragment declares defineDns like every service does.
38
- // Resource names are prefixed with the app prefix, so multiple apps can bootstrap
39
- // into one shared AWS account.
38
+ // Resource names are prefixed with the deployment's name, so multiple deployments
39
+ // can bootstrap into one shared AWS account.
40
40
  const getWorkspaceBootstrapQpqConfig = (ctx) => [
41
- (0, quidproquo_core_1.defineApplication)(ctx.prefix, ctx.environment, ctx.appDir, ctx.actorName),
41
+ (0, quidproquo_core_1.defineApplication)(ctx.name, ctx.environment, ctx.appDir, ctx.feature),
42
42
  (0, quidproquo_config_aws_1.defineAwsServiceAccountInfo)(ctx.accountId, ctx.region),
43
43
  ...loadAppConfigFragment(ctx, 'bootstrap.qpq.ts'),
44
44
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"getWorkspaceQpqConfigs.js","sourceRoot":"","sources":["../../../src/appWorkspace/getWorkspaceQpqConfigs.ts"],"names":[],"mappings":";;;;;;AAAA,iEAAoE;AACpE,qDAA+D;AAE/D,4CAAoB;AACpB,gDAAwB;AAIxB,0EAA0E;AAC1E,8EAA8E;AAC9E,qDAAqD;AACrD,8EAA8E;AAC9E,6EAA6E;AAC7E,8DAA8D;AAC9D,MAAM,qBAAqB,GAAG,CAAC,GAAwB,EAAE,QAAgB,EAAa,EAAE;IACtF,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,iEAAiE;IACjE,MAAM,cAAc,GAAG,OAAO,CAAC,cAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACrF,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,IAAI,cAAc,CAAC;IAE1D,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnE,CAAC,CAAC;AAEF,yEAAyE;AACzE,8EAA8E;AAC9E,uDAAuD;AAChD,MAAM,4BAA4B,GAAG,CAAC,GAAwB,EAAa,EAAE,CAAC;IACnF,IAAA,mCAAiB,EAAC,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC;IAEzD,IAAA,mDAA2B,EAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC;IAEtD,GAAG,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,CAAC;CAChD,CAAC;AANW,QAAA,4BAA4B,gCAMvC;AAEF,+EAA+E;AAC/E,iFAAiF;AACjF,kFAAkF;AAClF,+BAA+B;AACxB,MAAM,8BAA8B,GAAG,CAAC,GAAwB,EAAa,EAAE,CAAC;IACrF,IAAA,mCAAiB,EAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC;IAEzE,IAAA,mDAA2B,EAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC;IAEtD,GAAG,qBAAqB,CAAC,GAAG,EAAE,kBAAkB,CAAC;CAClD,CAAC;AANW,QAAA,8BAA8B,kCAMzC;AAEF,8EAA8E;AAC9E,8EAA8E;AAC9E,+DAA+D;AACxD,MAAM,4BAA4B,GAAG,CAAC,IAAY,EAAE,OAAe,EAAE,WAAmB,EAAa,EAAE;IAC5G,MAAM,kBAAkB,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC;IAEzH,iEAAiE;IACjE,MAAM,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAChD,OAAO,WAAW,CAAC,OAAO,IAAI,WAAW,CAAC;AAC5C,CAAC,CAAC;AANW,QAAA,4BAA4B,gCAMvC","sourcesContent":["import { defineAwsServiceAccountInfo } from 'quidproquo-config-aws';\nimport { defineApplication, QPQConfig } from 'quidproquo-core';\n\nimport fs from 'fs';\nimport path from 'path';\n\nimport { QpqAppDeployContext } from './qpqAppDeployConfig';\n\n// Loads an optional per-app config fragment (apps/<app>/account.qpq.ts or\n// bootstrap.qpq.ts) — a default-exported `(ctx) => QPQConfig` of app-specific\n// EXTRAS. The identity plumbing (defineApplication +\n// defineAwsServiceAccountInfo) is provided by the callers below, so fragments\n// must not declare their own. Requiring TS relies on the caller running with\n// TS require hooks (ts-node), as the CDK app invocation does.\nconst loadAppConfigFragment = (ctx: QpqAppDeployContext, fileName: string): QPQConfig => {\n const fragmentPath = path.join(ctx.appDir, fileName);\n if (!fs.existsSync(fragmentPath)) {\n return [];\n }\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fragmentModule = require(path.join(ctx.appDir, fileName.replace(/\\.ts$/, '')));\n const fragment = fragmentModule.default || fragmentModule;\n\n return typeof fragment === 'function' ? fragment(ctx) : fragment;\n};\n\n// Account-level guardrails — one statically named stack per AWS account,\n// independent of any app. The app name / environment here only satisfy config\n// plumbing; account resources key on accountId/region.\nexport const getWorkspaceAccountQpqConfig = (ctx: QpqAppDeployContext): QPQConfig => [\n defineApplication('account', ctx.environment, ctx.appDir),\n\n defineAwsServiceAccountInfo(ctx.accountId, ctx.region),\n\n ...loadAppConfigFragment(ctx, 'account.qpq.ts'),\n];\n\n// The app's bootstrap config: apex/zone/cert/WAF-level resources every service\n// in the app builds on. The fragment declares defineDns like every service does.\n// Resource names are prefixed with the app prefix, so multiple apps can bootstrap\n// into one shared AWS account.\nexport const getWorkspaceBootstrapQpqConfig = (ctx: QpqAppDeployContext): QPQConfig => [\n defineApplication(ctx.prefix, ctx.environment, ctx.appDir, ctx.actorName),\n\n defineAwsServiceAccountInfo(ctx.accountId, ctx.region),\n\n ...loadAppConfigFragment(ctx, 'bootstrap.qpq.ts'),\n];\n\n// A service's own QPQ config, loaded straight from its infrastructure.ts (its\n// @scope imports resolve through the workspace node_modules symlinks to built\n// dist — no tsconfig-paths registration needed under ts-node).\nexport const getWorkspaceServiceQpqConfig = (root: string, appName: string, serviceName: string): QPQConfig => {\n const infrastructurePath = path.join(root, 'apps', appName, 'services', serviceName, 'service', 'src', 'infrastructure');\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const infraModule = require(infrastructurePath);\n return infraModule.default || infraModule;\n};\n"]}
1
+ {"version":3,"file":"getWorkspaceQpqConfigs.js","sourceRoot":"","sources":["../../../src/appWorkspace/getWorkspaceQpqConfigs.ts"],"names":[],"mappings":";;;;;;AAAA,iEAAoE;AACpE,qDAA+D;AAE/D,4CAAoB;AACpB,gDAAwB;AAIxB,0EAA0E;AAC1E,8EAA8E;AAC9E,qDAAqD;AACrD,8EAA8E;AAC9E,6EAA6E;AAC7E,8DAA8D;AAC9D,MAAM,qBAAqB,GAAG,CAAC,GAAwB,EAAE,QAAgB,EAAa,EAAE;IACtF,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,iEAAiE;IACjE,MAAM,cAAc,GAAG,OAAO,CAAC,cAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACrF,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,IAAI,cAAc,CAAC;IAE1D,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnE,CAAC,CAAC;AAEF,yEAAyE;AACzE,8EAA8E;AAC9E,uDAAuD;AAChD,MAAM,4BAA4B,GAAG,CAAC,GAAwB,EAAa,EAAE,CAAC;IACnF,IAAA,mCAAiB,EAAC,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC;IAEzD,IAAA,mDAA2B,EAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC;IAEtD,GAAG,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,CAAC;CAChD,CAAC;AANW,QAAA,4BAA4B,gCAMvC;AAEF,+EAA+E;AAC/E,iFAAiF;AACjF,kFAAkF;AAClF,6CAA6C;AACtC,MAAM,8BAA8B,GAAG,CAAC,GAAwB,EAAa,EAAE,CAAC;IACrF,IAAA,mCAAiB,EAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC;IAErE,IAAA,mDAA2B,EAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC;IAEtD,GAAG,qBAAqB,CAAC,GAAG,EAAE,kBAAkB,CAAC;CAClD,CAAC;AANW,QAAA,8BAA8B,kCAMzC;AAEF,8EAA8E;AAC9E,8EAA8E;AAC9E,+DAA+D;AACxD,MAAM,4BAA4B,GAAG,CAAC,IAAY,EAAE,OAAe,EAAE,WAAmB,EAAa,EAAE;IAC5G,MAAM,kBAAkB,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC;IAEzH,iEAAiE;IACjE,MAAM,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAChD,OAAO,WAAW,CAAC,OAAO,IAAI,WAAW,CAAC;AAC5C,CAAC,CAAC;AANW,QAAA,4BAA4B,gCAMvC","sourcesContent":["import { defineAwsServiceAccountInfo } from 'quidproquo-config-aws';\nimport { defineApplication, QPQConfig } from 'quidproquo-core';\n\nimport fs from 'fs';\nimport path from 'path';\n\nimport { QpqAppDeployContext } from './QpqAppDeployContext';\n\n// Loads an optional per-app config fragment (apps/<app>/account.qpq.ts or\n// bootstrap.qpq.ts) — a default-exported `(ctx) => QPQConfig` of app-specific\n// EXTRAS. The identity plumbing (defineApplication +\n// defineAwsServiceAccountInfo) is provided by the callers below, so fragments\n// must not declare their own. Requiring TS relies on the caller running with\n// TS require hooks (ts-node), as the CDK app invocation does.\nconst loadAppConfigFragment = (ctx: QpqAppDeployContext, fileName: string): QPQConfig => {\n const fragmentPath = path.join(ctx.appDir, fileName);\n if (!fs.existsSync(fragmentPath)) {\n return [];\n }\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fragmentModule = require(path.join(ctx.appDir, fileName.replace(/\\.ts$/, '')));\n const fragment = fragmentModule.default || fragmentModule;\n\n return typeof fragment === 'function' ? fragment(ctx) : fragment;\n};\n\n// Account-level guardrails — one statically named stack per AWS account,\n// independent of any app. The app name / environment here only satisfy config\n// plumbing; account resources key on accountId/region.\nexport const getWorkspaceAccountQpqConfig = (ctx: QpqAppDeployContext): QPQConfig => [\n defineApplication('account', ctx.environment, ctx.appDir),\n\n defineAwsServiceAccountInfo(ctx.accountId, ctx.region),\n\n ...loadAppConfigFragment(ctx, 'account.qpq.ts'),\n];\n\n// The app's bootstrap config: apex/zone/cert/WAF-level resources every service\n// in the app builds on. The fragment declares defineDns like every service does.\n// Resource names are prefixed with the deployment's name, so multiple deployments\n// can bootstrap into one shared AWS account.\nexport const getWorkspaceBootstrapQpqConfig = (ctx: QpqAppDeployContext): QPQConfig => [\n defineApplication(ctx.name, ctx.environment, ctx.appDir, ctx.feature),\n\n defineAwsServiceAccountInfo(ctx.accountId, ctx.region),\n\n ...loadAppConfigFragment(ctx, 'bootstrap.qpq.ts'),\n];\n\n// A service's own QPQ config, loaded straight from its infrastructure.ts (its\n// @scope imports resolve through the workspace node_modules symlinks to built\n// dist — no tsconfig-paths registration needed under ts-node).\nexport const getWorkspaceServiceQpqConfig = (root: string, appName: string, serviceName: string): QPQConfig => {\n const infrastructurePath = path.join(root, 'apps', appName, 'services', serviceName, 'service', 'src', 'infrastructure');\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const infraModule = require(infrastructurePath);\n return infraModule.default || infraModule;\n};\n"]}
@@ -1,4 +1,5 @@
1
1
  export * from './createWorkspaceQpqCdkApp';
2
+ export * from './getQpqAppDeployContext';
2
3
  export * from './getWorkspaceQpqConfigs';
3
- export * from './qpqAppDeployConfig';
4
+ export * from './QpqAppDeployContext';
4
5
  export * from './requireDomainResolver';
@@ -15,7 +15,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./createWorkspaceQpqCdkApp"), exports);
18
+ __exportStar(require("./getQpqAppDeployContext"), exports);
18
19
  __exportStar(require("./getWorkspaceQpqConfigs"), exports);
19
- __exportStar(require("./qpqAppDeployConfig"), exports);
20
+ __exportStar(require("./QpqAppDeployContext"), exports);
20
21
  __exportStar(require("./requireDomainResolver"), exports);
21
22
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/appWorkspace/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6DAA2C;AAC3C,2DAAyC;AACzC,uDAAqC;AACrC,0DAAwC","sourcesContent":["export * from './createWorkspaceQpqCdkApp';\nexport * from './getWorkspaceQpqConfigs';\nexport * from './qpqAppDeployConfig';\nexport * from './requireDomainResolver';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/appWorkspace/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6DAA2C;AAC3C,2DAAyC;AACzC,2DAAyC;AACzC,wDAAsC;AACtC,0DAAwC","sourcesContent":["export * from './createWorkspaceQpqCdkApp';\nexport * from './getQpqAppDeployContext';\nexport * from './getWorkspaceQpqConfigs';\nexport * from './QpqAppDeployContext';\nexport * from './requireDomainResolver';\n"]}
@@ -139,11 +139,12 @@ class QpqCoreKeyValueStoreConstruct extends QpqCoreKeyValueStoreConstructBase {
139
139
  sortKey: (0, exports.convertKvsKeyToDynamodbAttribute)(sortKey),
140
140
  });
141
141
  }
142
- // Do global secondary indexes
142
+ // Do global secondary indexes. A scoped store's are keyed on hidden scope-composed copies of
143
+ // their partition keys, so each partition holds one scope's rows.
143
144
  for (const index of props.keyValueStoreConfig.indexes) {
144
145
  table.addGlobalSecondaryIndex({
145
- indexName: index.partitionKey.key,
146
- partitionKey: (0, exports.convertKvsKeyToDynamodbAttribute)(index.partitionKey),
146
+ indexName: index.name,
147
+ partitionKey: (0, exports.convertKvsKeyToDynamodbAttribute)(quidproquo_actionprocessor_awslambda_1.awsNamingUtils.getKvsIndexPartitionKey(props.keyValueStoreConfig, index)),
147
148
  sortKey: index.sortKey && (0, exports.convertKvsKeyToDynamodbAttribute)(index.sortKey),
148
149
  });
149
150
  }
@@ -1 +1 @@
1
- {"version":3,"file":"QpqCoreKeyValueStoreConstruct.js","sourceRoot":"","sources":["../../../../../../src/constructs/feature/core/keyValueStore/QpqCoreKeyValueStoreConstruct.ts"],"names":[],"mappings":"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","sourcesContent":["import { awsNamingUtils } from 'quidproquo-actionprocessor-awslambda';\nimport { AwsDataStoreRemovalPolicy, qpqConfigAwsUtils } from 'quidproquo-config-aws';\nimport { KeyValueStoreQPQConfigSetting, KvsKey, QPQConfig, qpqCoreUtils } from 'quidproquo-core';\n\nimport { aws_dynamodb, aws_iam, aws_kms, aws_ssm } from 'aws-cdk-lib';\nimport * as cdk from 'aws-cdk-lib';\nimport { Construct } from 'constructs';\n\nimport * as qpqDeployAwsCdkUtils from '../../../../utils/qpqDeployAwsCdkUtils';\nimport { createDefaultResourceAlarm } from '../../../base/createDefaultResourceAlarm';\nimport { QpqConstructBlock, QpqConstructBlockProps } from '../../../base/QpqConstructBlock';\n\nexport interface QpqCoreKeyValueStoreConstructProps extends QpqConstructBlockProps {\n keyValueStoreConfig: KeyValueStoreQPQConfigSetting;\n\n /**\n * The key behind the store's `cryptoKeyName`, resolved by the stack (see\n * resolveCryptoKeyForResource). Required when the store names one.\n */\n cryptoKey?: aws_kms.IKey;\n}\n\nexport abstract class QpqCoreKeyValueStoreConstructBase extends QpqConstructBlock {\n abstract table: aws_dynamodb.ITable;\n\n public grantRead(grantee: aws_iam.IGrantable) {\n this.table.grantReadData(grantee);\n }\n\n public grantWrite(grantee: aws_iam.IGrantable) {\n this.table.grantWriteData(grantee);\n }\n\n public grantAll(grantee: aws_iam.IGrantable) {\n this.table.grantFullAccess(grantee);\n }\n}\n\n// Deterministic SSM location for a store's stream ARN, derived from the table's own\n// resource name so both stacks compute it without talking to each other.\nexport const kvsStreamArnSsmParameterName = (tableResourceName: string): string => `/qpq/kvs-stream-arn/${tableResourceName}`;\n\nexport const convertKvsKeyTypeToDynamodbAttributeType = (kvsKeyType: KvsKey['type']): aws_dynamodb.AttributeType => {\n switch (kvsKeyType) {\n case 'string':\n return aws_dynamodb.AttributeType.STRING;\n case 'number':\n return aws_dynamodb.AttributeType.NUMBER;\n case 'binary':\n return aws_dynamodb.AttributeType.BINARY;\n }\n\n throw new Error(`Unknown KvsKeyType: ${kvsKeyType}`);\n};\n\nexport const convertKvsKeyToDynamodbAttribute = (kvsKey: KvsKey): aws_dynamodb.Attribute => ({\n name: kvsKey.key,\n type: convertKvsKeyTypeToDynamodbAttributeType(kvsKey.type),\n});\n\nexport class QpqCoreKeyValueStoreConstruct extends QpqCoreKeyValueStoreConstructBase {\n table: aws_dynamodb.ITable;\n\n static fromOtherStack(scope: Construct, id: string, qpqConfig: QPQConfig, keyValueStoreName: string): QpqCoreKeyValueStoreConstructBase {\n const tableNameOverride = qpqConfigAwsUtils.getDynamoTableNameOverrride(keyValueStoreName, qpqConfig);\n\n class Import extends QpqCoreKeyValueStoreConstructBase {\n table = aws_dynamodb.Table.fromTableName(this, 'table', tableNameOverride || this.qpqResourceName(keyValueStoreName, 'kvs'));\n }\n\n return new Import(scope, id, { qpqConfig });\n }\n\n constructor(scope: Construct, id: string, props: QpqCoreKeyValueStoreConstructProps) {\n super(scope, id, props);\n\n const dataStoreRemovalPolicy = qpqConfigAwsUtils.getAwsDataStoreRemovalPolicy(props.qpqConfig);\n\n const [primarySortKey] = props.keyValueStoreConfig.sortKeys;\n\n if (props.keyValueStoreConfig.cryptoKeyName && !props.cryptoKey) {\n throw new Error(\n `Key value store \"${props.keyValueStoreConfig.keyValueStoreName}\" names crypto key \"${props.keyValueStoreConfig.cryptoKeyName}\" but none was resolved`,\n );\n }\n const tableEncryption = props.cryptoKey ? aws_dynamodb.TableEncryption.CUSTOMER_MANAGED : undefined;\n const encryptionKey = props.cryptoKey;\n\n const table = new aws_dynamodb.Table(this, 'table', {\n tableName: this.qpqResourceName(props.keyValueStoreConfig.keyValueStoreName, 'kvs'),\n partitionKey: convertKvsKeyToDynamodbAttribute(props.keyValueStoreConfig.partitionKey),\n sortKey: primarySortKey && convertKvsKeyToDynamodbAttribute(primarySortKey),\n billingMode: aws_dynamodb.BillingMode.PAY_PER_REQUEST,\n // Retain data stores by default; dev configs opt into full teardown via defineAwsDataStoreRemovalPolicy(destroy).\n // When retained, deletionProtection also blocks deletes/replacements outright (RETAIN alone only orphans the table).\n removalPolicy: dataStoreRemovalPolicy === AwsDataStoreRemovalPolicy.destroy ? cdk.RemovalPolicy.DESTROY : cdk.RemovalPolicy.RETAIN,\n deletionProtection: dataStoreRemovalPolicy === AwsDataStoreRemovalPolicy.retain,\n timeToLiveAttribute: props.keyValueStoreConfig.ttlAttribute,\n // PITR on by default (35-day continuous backups / point-in-time restore); opt out per-table via config.\n pointInTimeRecoverySpecification: { pointInTimeRecoveryEnabled: !props.keyValueStoreConfig.disablePointInTimeRecovery },\n encryption: tableEncryption,\n encryptionKey,\n // Change data capture, only when a handler is configured — a stream on a table\n // nothing consumes is pure cost. NEW_AND_OLD_IMAGES so a handler can see what a\n // change actually did, including on a delete, where the new image does not exist.\n stream: props.keyValueStoreConfig.onStream ? aws_dynamodb.StreamViewType.NEW_AND_OLD_IMAGES : undefined,\n });\n\n qpqDeployAwsCdkUtils.applyEnvironmentTags(table, props.qpqConfig);\n\n // Default alarms (opt-in via defineNotifyError): read/write throttling means\n // the table is rejecting requests (hot partition / capacity), user-visible.\n createDefaultResourceAlarm(this, props.qpqConfig, {\n id: 'default-alarm-read-throttle',\n alarmName: this.resourceName(`${props.keyValueStoreConfig.keyValueStoreName}-read-throttle`),\n metric: table.metric('ReadThrottleEvents', { period: cdk.Duration.minutes(1), statistic: 'Sum' }),\n threshold: 1,\n evaluationPeriods: 1,\n });\n createDefaultResourceAlarm(this, props.qpqConfig, {\n id: 'default-alarm-write-throttle',\n alarmName: this.resourceName(`${props.keyValueStoreConfig.keyValueStoreName}-write-throttle`),\n metric: table.metric('WriteThrottleEvents', { period: cdk.Duration.minutes(1), statistic: 'Sum' }),\n threshold: 1,\n evaluationPeriods: 1,\n });\n\n // Do local secondary indexes\n for (let i = 1; i < props.keyValueStoreConfig.sortKeys.length; i++) {\n let sortKey = props.keyValueStoreConfig.sortKeys[i];\n\n table.addLocalSecondaryIndex({\n indexName: sortKey.key,\n sortKey: convertKvsKeyToDynamodbAttribute(sortKey),\n });\n }\n\n // Do global secondary indexes\n for (const index of props.keyValueStoreConfig.indexes) {\n table.addGlobalSecondaryIndex({\n indexName: index.partitionKey.key,\n partitionKey: convertKvsKeyToDynamodbAttribute(index.partitionKey),\n sortKey: index.sortKey && convertKvsKeyToDynamodbAttribute(index.sortKey),\n });\n }\n\n this.table = table;\n\n // Publish the stream ARN for the API stack's handler to pick up.\n //\n // A DynamoDB stream ARN ends in a creation timestamp, so unlike every other resource\n // here it CANNOT be recomputed from naming conventions — which is how the API stack\n // reaches every other table (fromOtherStack). Passing the table object across stacks\n // would work but creates a CloudFormation export, the coupling this codebase avoids\n // everywhere else. So it goes through SSM and is resolved at deploy time, the same\n // trick DomainCertificateLookup uses for cert ARNs.\n if (props.keyValueStoreConfig.onStream) {\n new aws_ssm.StringParameter(this, 'stream-arn', {\n parameterName: kvsStreamArnSsmParameterName(this.qpqResourceName(props.keyValueStoreConfig.keyValueStoreName, 'kvs')),\n stringValue: table.tableStreamArn!,\n });\n }\n\n // If we have an override, lets use it instead\n // we still want to keep the above table created.\n const tableNameOverride = qpqConfigAwsUtils.getDynamoTableNameOverrride(props.keyValueStoreConfig.keyValueStoreName, props.qpqConfig);\n if (tableNameOverride) {\n console.log('tableNameOverride', tableNameOverride);\n this.table = aws_dynamodb.Table.fromTableName(this, 'tablelink', tableNameOverride);\n }\n }\n\n public static authorizeActionsForRole(scope: Construct, role: aws_iam.IRole, qpqConfig: QPQConfig) {\n const kvsActions = [\n 'dynamodb:GetItem',\n 'dynamodb:PutItem',\n 'dynamodb:BatchWriteItem',\n 'dynamodb:Query',\n 'dynamodb:Scan',\n 'dynamodb:UpdateItem',\n 'dynamodb:DeleteItem',\n ];\n\n const region = qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);\n const accountId = qpqConfigAwsUtils.getApplicationModuleDeployAccountId(qpqConfig);\n\n // Owned tables are granted as a single tag-conditioned statement instead of one ARN\n // pair per table: applyEnvironmentTags stamps every owned table, and DynamoDB ABAC\n // evaluates the table's tags for its index sub-resources too. This keeps the policy\n // a fixed size no matter how many KVSs (eventDoc collections especially) the service\n // accumulates. A table brought in via getDynamoTableNameOverrride is imported, not\n // created here, so it only matches if whoever created it applied the same tags.\n role.addToPrincipalPolicy(\n new aws_iam.PolicyStatement({\n effect: aws_iam.Effect.ALLOW,\n actions: kvsActions,\n resources: [`arn:aws:dynamodb:${region}:${accountId}:table/*`, `arn:aws:dynamodb:${region}:${accountId}:table/*/index/*`],\n conditions: {\n StringEquals: qpqDeployAwsCdkUtils.getOwnedResourceTagConditions(qpqConfig),\n },\n }),\n );\n\n // Deterministically-computed ARNs for KVSs declared in this service's\n // config but owned by another service. Uses the same naming path as\n // `getKvsDynamoTableNameFromConfig` so no CDK cross-stack ref is created.\n // Cross-service access stays as exact ARNs on purpose: this short list is\n // the part of the policy a human should be reviewing.\n const allKvsConfigs = qpqCoreUtils.getAllKeyValueStores(qpqConfig);\n const ownedKvsConfigs = qpqCoreUtils.getOwnedKeyValueStores(qpqConfig);\n const foreignKvsConfigs = allKvsConfigs.filter((cfg) => !ownedKvsConfigs.includes(cfg));\n\n const foreignArns = foreignKvsConfigs.flatMap((cfg) => {\n const tableName = awsNamingUtils.getKvsDynamoTableNameFromConfig(cfg.keyValueStoreName, qpqConfig, 'kvs');\n const tableArn = `arn:aws:dynamodb:*:*:table/${tableName}`;\n return [tableArn, `${tableArn}/index/*`];\n });\n\n if (foreignArns.length > 0) {\n qpqDeployAwsCdkUtils.attachManagedResourcePolicies(scope, role, 'webserverKeyValueStoreAccess', kvsActions, foreignArns);\n }\n\n // KMS use for a store's cryptoKeyName comes from QpqCoreCryptoKeyConstruct.authorizeActionsForRole,\n // which covers every crypto key declared in this config.\n }\n}\n"]}
1
+ {"version":3,"file":"QpqCoreKeyValueStoreConstruct.js","sourceRoot":"","sources":["../../../../../../src/constructs/feature/core/keyValueStore/QpqCoreKeyValueStoreConstruct.ts"],"names":[],"mappings":"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","sourcesContent":["import { awsNamingUtils } from 'quidproquo-actionprocessor-awslambda';\nimport { AwsDataStoreRemovalPolicy, qpqConfigAwsUtils } from 'quidproquo-config-aws';\nimport { KeyValueStoreQPQConfigSetting, KvsKey, QPQConfig, qpqCoreUtils } from 'quidproquo-core';\n\nimport { aws_dynamodb, aws_iam, aws_kms, aws_ssm } from 'aws-cdk-lib';\nimport * as cdk from 'aws-cdk-lib';\nimport { Construct } from 'constructs';\n\nimport * as qpqDeployAwsCdkUtils from '../../../../utils/qpqDeployAwsCdkUtils';\nimport { createDefaultResourceAlarm } from '../../../base/createDefaultResourceAlarm';\nimport { QpqConstructBlock, QpqConstructBlockProps } from '../../../base/QpqConstructBlock';\n\nexport interface QpqCoreKeyValueStoreConstructProps extends QpqConstructBlockProps {\n keyValueStoreConfig: KeyValueStoreQPQConfigSetting;\n\n /**\n * The key behind the store's `cryptoKeyName`, resolved by the stack (see\n * resolveCryptoKeyForResource). Required when the store names one.\n */\n cryptoKey?: aws_kms.IKey;\n}\n\nexport abstract class QpqCoreKeyValueStoreConstructBase extends QpqConstructBlock {\n abstract table: aws_dynamodb.ITable;\n\n public grantRead(grantee: aws_iam.IGrantable) {\n this.table.grantReadData(grantee);\n }\n\n public grantWrite(grantee: aws_iam.IGrantable) {\n this.table.grantWriteData(grantee);\n }\n\n public grantAll(grantee: aws_iam.IGrantable) {\n this.table.grantFullAccess(grantee);\n }\n}\n\n// Deterministic SSM location for a store's stream ARN, derived from the table's own\n// resource name so both stacks compute it without talking to each other.\nexport const kvsStreamArnSsmParameterName = (tableResourceName: string): string => `/qpq/kvs-stream-arn/${tableResourceName}`;\n\nexport const convertKvsKeyTypeToDynamodbAttributeType = (kvsKeyType: KvsKey['type']): aws_dynamodb.AttributeType => {\n switch (kvsKeyType) {\n case 'string':\n return aws_dynamodb.AttributeType.STRING;\n case 'number':\n return aws_dynamodb.AttributeType.NUMBER;\n case 'binary':\n return aws_dynamodb.AttributeType.BINARY;\n }\n\n throw new Error(`Unknown KvsKeyType: ${kvsKeyType}`);\n};\n\nexport const convertKvsKeyToDynamodbAttribute = (kvsKey: KvsKey): aws_dynamodb.Attribute => ({\n name: kvsKey.key,\n type: convertKvsKeyTypeToDynamodbAttributeType(kvsKey.type),\n});\n\nexport class QpqCoreKeyValueStoreConstruct extends QpqCoreKeyValueStoreConstructBase {\n table: aws_dynamodb.ITable;\n\n static fromOtherStack(scope: Construct, id: string, qpqConfig: QPQConfig, keyValueStoreName: string): QpqCoreKeyValueStoreConstructBase {\n const tableNameOverride = qpqConfigAwsUtils.getDynamoTableNameOverrride(keyValueStoreName, qpqConfig);\n\n class Import extends QpqCoreKeyValueStoreConstructBase {\n table = aws_dynamodb.Table.fromTableName(this, 'table', tableNameOverride || this.qpqResourceName(keyValueStoreName, 'kvs'));\n }\n\n return new Import(scope, id, { qpqConfig });\n }\n\n constructor(scope: Construct, id: string, props: QpqCoreKeyValueStoreConstructProps) {\n super(scope, id, props);\n\n const dataStoreRemovalPolicy = qpqConfigAwsUtils.getAwsDataStoreRemovalPolicy(props.qpqConfig);\n\n const [primarySortKey] = props.keyValueStoreConfig.sortKeys;\n\n if (props.keyValueStoreConfig.cryptoKeyName && !props.cryptoKey) {\n throw new Error(\n `Key value store \"${props.keyValueStoreConfig.keyValueStoreName}\" names crypto key \"${props.keyValueStoreConfig.cryptoKeyName}\" but none was resolved`,\n );\n }\n const tableEncryption = props.cryptoKey ? aws_dynamodb.TableEncryption.CUSTOMER_MANAGED : undefined;\n const encryptionKey = props.cryptoKey;\n\n const table = new aws_dynamodb.Table(this, 'table', {\n tableName: this.qpqResourceName(props.keyValueStoreConfig.keyValueStoreName, 'kvs'),\n partitionKey: convertKvsKeyToDynamodbAttribute(props.keyValueStoreConfig.partitionKey),\n sortKey: primarySortKey && convertKvsKeyToDynamodbAttribute(primarySortKey),\n billingMode: aws_dynamodb.BillingMode.PAY_PER_REQUEST,\n // Retain data stores by default; dev configs opt into full teardown via defineAwsDataStoreRemovalPolicy(destroy).\n // When retained, deletionProtection also blocks deletes/replacements outright (RETAIN alone only orphans the table).\n removalPolicy: dataStoreRemovalPolicy === AwsDataStoreRemovalPolicy.destroy ? cdk.RemovalPolicy.DESTROY : cdk.RemovalPolicy.RETAIN,\n deletionProtection: dataStoreRemovalPolicy === AwsDataStoreRemovalPolicy.retain,\n timeToLiveAttribute: props.keyValueStoreConfig.ttlAttribute,\n // PITR on by default (35-day continuous backups / point-in-time restore); opt out per-table via config.\n pointInTimeRecoverySpecification: { pointInTimeRecoveryEnabled: !props.keyValueStoreConfig.disablePointInTimeRecovery },\n encryption: tableEncryption,\n encryptionKey,\n // Change data capture, only when a handler is configured — a stream on a table\n // nothing consumes is pure cost. NEW_AND_OLD_IMAGES so a handler can see what a\n // change actually did, including on a delete, where the new image does not exist.\n stream: props.keyValueStoreConfig.onStream ? aws_dynamodb.StreamViewType.NEW_AND_OLD_IMAGES : undefined,\n });\n\n qpqDeployAwsCdkUtils.applyEnvironmentTags(table, props.qpqConfig);\n\n // Default alarms (opt-in via defineNotifyError): read/write throttling means\n // the table is rejecting requests (hot partition / capacity), user-visible.\n createDefaultResourceAlarm(this, props.qpqConfig, {\n id: 'default-alarm-read-throttle',\n alarmName: this.resourceName(`${props.keyValueStoreConfig.keyValueStoreName}-read-throttle`),\n metric: table.metric('ReadThrottleEvents', { period: cdk.Duration.minutes(1), statistic: 'Sum' }),\n threshold: 1,\n evaluationPeriods: 1,\n });\n createDefaultResourceAlarm(this, props.qpqConfig, {\n id: 'default-alarm-write-throttle',\n alarmName: this.resourceName(`${props.keyValueStoreConfig.keyValueStoreName}-write-throttle`),\n metric: table.metric('WriteThrottleEvents', { period: cdk.Duration.minutes(1), statistic: 'Sum' }),\n threshold: 1,\n evaluationPeriods: 1,\n });\n\n // Do local secondary indexes\n for (let i = 1; i < props.keyValueStoreConfig.sortKeys.length; i++) {\n let sortKey = props.keyValueStoreConfig.sortKeys[i];\n\n table.addLocalSecondaryIndex({\n indexName: sortKey.key,\n sortKey: convertKvsKeyToDynamodbAttribute(sortKey),\n });\n }\n\n // Do global secondary indexes. A scoped store's are keyed on hidden scope-composed copies of\n // their partition keys, so each partition holds one scope's rows.\n for (const index of props.keyValueStoreConfig.indexes) {\n table.addGlobalSecondaryIndex({\n indexName: index.name,\n partitionKey: convertKvsKeyToDynamodbAttribute(awsNamingUtils.getKvsIndexPartitionKey(props.keyValueStoreConfig, index)),\n sortKey: index.sortKey && convertKvsKeyToDynamodbAttribute(index.sortKey),\n });\n }\n\n this.table = table;\n\n // Publish the stream ARN for the API stack's handler to pick up.\n //\n // A DynamoDB stream ARN ends in a creation timestamp, so unlike every other resource\n // here it CANNOT be recomputed from naming conventions — which is how the API stack\n // reaches every other table (fromOtherStack). Passing the table object across stacks\n // would work but creates a CloudFormation export, the coupling this codebase avoids\n // everywhere else. So it goes through SSM and is resolved at deploy time, the same\n // trick DomainCertificateLookup uses for cert ARNs.\n if (props.keyValueStoreConfig.onStream) {\n new aws_ssm.StringParameter(this, 'stream-arn', {\n parameterName: kvsStreamArnSsmParameterName(this.qpqResourceName(props.keyValueStoreConfig.keyValueStoreName, 'kvs')),\n stringValue: table.tableStreamArn!,\n });\n }\n\n // If we have an override, lets use it instead\n // we still want to keep the above table created.\n const tableNameOverride = qpqConfigAwsUtils.getDynamoTableNameOverrride(props.keyValueStoreConfig.keyValueStoreName, props.qpqConfig);\n if (tableNameOverride) {\n console.log('tableNameOverride', tableNameOverride);\n this.table = aws_dynamodb.Table.fromTableName(this, 'tablelink', tableNameOverride);\n }\n }\n\n public static authorizeActionsForRole(scope: Construct, role: aws_iam.IRole, qpqConfig: QPQConfig) {\n const kvsActions = [\n 'dynamodb:GetItem',\n 'dynamodb:PutItem',\n 'dynamodb:BatchWriteItem',\n 'dynamodb:Query',\n 'dynamodb:Scan',\n 'dynamodb:UpdateItem',\n 'dynamodb:DeleteItem',\n ];\n\n const region = qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);\n const accountId = qpqConfigAwsUtils.getApplicationModuleDeployAccountId(qpqConfig);\n\n // Owned tables are granted as a single tag-conditioned statement instead of one ARN\n // pair per table: applyEnvironmentTags stamps every owned table, and DynamoDB ABAC\n // evaluates the table's tags for its index sub-resources too. This keeps the policy\n // a fixed size no matter how many KVSs (eventDoc collections especially) the service\n // accumulates. A table brought in via getDynamoTableNameOverrride is imported, not\n // created here, so it only matches if whoever created it applied the same tags.\n role.addToPrincipalPolicy(\n new aws_iam.PolicyStatement({\n effect: aws_iam.Effect.ALLOW,\n actions: kvsActions,\n resources: [`arn:aws:dynamodb:${region}:${accountId}:table/*`, `arn:aws:dynamodb:${region}:${accountId}:table/*/index/*`],\n conditions: {\n StringEquals: qpqDeployAwsCdkUtils.getOwnedResourceTagConditions(qpqConfig),\n },\n }),\n );\n\n // Deterministically-computed ARNs for KVSs declared in this service's\n // config but owned by another service. Uses the same naming path as\n // `getKvsDynamoTableNameFromConfig` so no CDK cross-stack ref is created.\n // Cross-service access stays as exact ARNs on purpose: this short list is\n // the part of the policy a human should be reviewing.\n const allKvsConfigs = qpqCoreUtils.getAllKeyValueStores(qpqConfig);\n const ownedKvsConfigs = qpqCoreUtils.getOwnedKeyValueStores(qpqConfig);\n const foreignKvsConfigs = allKvsConfigs.filter((cfg) => !ownedKvsConfigs.includes(cfg));\n\n const foreignArns = foreignKvsConfigs.flatMap((cfg) => {\n const tableName = awsNamingUtils.getKvsDynamoTableNameFromConfig(cfg.keyValueStoreName, qpqConfig, 'kvs');\n const tableArn = `arn:aws:dynamodb:*:*:table/${tableName}`;\n return [tableArn, `${tableArn}/index/*`];\n });\n\n if (foreignArns.length > 0) {\n qpqDeployAwsCdkUtils.attachManagedResourcePolicies(scope, role, 'webserverKeyValueStoreAccess', kvsActions, foreignArns);\n }\n\n // KMS use for a store's cryptoKeyName comes from QpqCoreCryptoKeyConstruct.authorizeActionsForRole,\n // which covers every crypto key declared in this config.\n }\n}\n"]}
@@ -0,0 +1,16 @@
1
+ import { QpqAppDeploySettings } from 'quidproquo-core';
2
+ /**
3
+ * Deploy-time context handed to the app's account.qpq.ts / bootstrap.qpq.ts config
4
+ * fragments. Only AWS deployments reach the CDK app, so the AWS identity is required.
5
+ */
6
+ export type QpqAppDeployContext = {
7
+ appName: string;
8
+ appDir: string;
9
+ deploymentName: string;
10
+ name: string;
11
+ environment: string;
12
+ feature?: string;
13
+ settings: QpqAppDeploySettings;
14
+ accountId: string;
15
+ region: string;
16
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=QpqAppDeployContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QpqAppDeployContext.js","sourceRoot":"","sources":["../../../src/appWorkspace/QpqAppDeployContext.ts"],"names":[],"mappings":"","sourcesContent":["import { QpqAppDeploySettings } from 'quidproquo-core';\n\n/**\n * Deploy-time context handed to the app's account.qpq.ts / bootstrap.qpq.ts config\n * fragments. Only AWS deployments reach the CDK app, so the AWS identity is required.\n */\nexport type QpqAppDeployContext = {\n appName: string;\n appDir: string;\n\n deploymentName: string;\n\n name: string;\n environment: string;\n feature?: string;\n settings: QpqAppDeploySettings;\n\n accountId: string;\n region: string;\n};\n"]}
@@ -1,3 +1,2 @@
1
1
  import * as cdk from 'aws-cdk-lib';
2
- export declare const findWorkspaceRoot: (startDir?: string) => string;
3
2
  export declare const createWorkspaceQpqCdkApp: () => cdk.App;
@@ -1,45 +1,35 @@
1
- import fs from 'fs';
2
- import path from 'path';
1
+ import { getQpqAppDeployment, primeAwsDeploymentEnv, primeDeploymentEnv, validateAwsDeployment } from 'quidproquo-config-aws';
2
+ import { QpqDeployEnvVar } from 'quidproquo-core';
3
3
  import { createQPQApp } from '../QPQApp';
4
4
  import { AccountQpqStack, ApiQpqServiceStack, BootstrapQpqServiceStack, DomainQpqServiceStack, InfQpqServiceStack, WebQpqServiceStack, } from '../stacks';
5
5
  import * as qpqDeployAwsCdkUtils from '../utils';
6
+ import { getQpqAppDeployContext } from './getQpqAppDeployContext';
6
7
  import { getWorkspaceAccountQpqConfig, getWorkspaceBootstrapQpqConfig, getWorkspaceServiceQpqConfig } from './getWorkspaceQpqConfigs';
7
- import { getQpqAppDeployContext } from './qpqAppDeployConfig';
8
- // Walk up from cwd to the workspace root (the directory with an apps/ folder).
9
- export const findWorkspaceRoot = (startDir = process.cwd()) => {
10
- let dir = startDir;
11
- while (!fs.existsSync(path.join(dir, 'apps')) || !fs.existsSync(path.join(dir, 'package.json'))) {
12
- const parent = path.dirname(dir);
13
- if (parent === dir)
14
- throw new Error('Could not find workspace root (a directory containing apps/ and package.json)');
15
- dir = parent;
16
- }
17
- return dir;
18
- };
19
- // The generic CDK app for a QPQ app workspace — the same stacks for every
8
+ // The generic CDK app for a QPQ app workspace: the same stacks for every
20
9
  // product; everything app-specific comes from apps/<app>/ (deploy.config.json
21
10
  // + optional account.qpq.ts / bootstrap.qpq.ts fragments).
22
11
  //
23
- // Driven by environment variables set by the invoking tool (`qpq go`):
24
- // DEPLOY_APP_NAME (required) app folder under apps/
25
- // DEPLOY_SERVICE_NAME (optional) also synth the service's inf/web/api stacks
26
- // ENVIRONMENT (required) environment name in deploy.config.json
27
- // ACTOR_NAME (optional) actor/feature deploys
28
- // AWS_DEFAULT_ACCOUNT / AWS_DEFAULT_REGION (optional) identity override
12
+ // Driven by two env vars set by the invoking tool (`qpq go`):
13
+ // DEPLOY_APP_NAME app folder under apps/
14
+ // DEPLOY_NAME deployment name in that app's deploy.config.json
15
+ // plus DEPLOY_SERVICE_NAME (optional) to also synth a service's inf/web/api
16
+ // stacks. Everything else (identity, environment, feature, settings) comes from
17
+ // the deployment entry, primed here before any service config is required.
29
18
  export const createWorkspaceQpqCdkApp = () => {
30
- const root = findWorkspaceRoot();
31
- const deployAppName = process.env.DEPLOY_APP_NAME;
19
+ // qpq spawns cdk with cwd at the workspace root.
20
+ const root = process.cwd();
21
+ const deployAppName = process.env[QpqDeployEnvVar.deployAppName];
32
22
  if (!deployAppName) {
33
- throw new Error('DEPLOY_APP_NAME environment variable is not set.');
23
+ throw new Error(`${QpqDeployEnvVar.deployAppName} environment variable is not set.`);
34
24
  }
35
- const environment = process.env.ENVIRONMENT;
36
- if (!environment) {
37
- throw new Error('ENVIRONMENT environment variable is not set.');
25
+ const deploymentName = process.env[QpqDeployEnvVar.deployName];
26
+ if (!deploymentName) {
27
+ throw new Error(`${QpqDeployEnvVar.deployName} environment variable is not set.`);
38
28
  }
39
- const ctx = getQpqAppDeployContext(root, deployAppName, environment, process.env.ACTOR_NAME, {
40
- accountId: process.env.AWS_DEFAULT_ACCOUNT,
41
- region: process.env.AWS_DEFAULT_REGION,
42
- });
29
+ const deployment = validateAwsDeployment(deploymentName, getQpqAppDeployment(root, deployAppName, deploymentName));
30
+ primeDeploymentEnv(deployAppName, deploymentName, deployment);
31
+ primeAwsDeploymentEnv(deployment);
32
+ const ctx = getQpqAppDeployContext(root, deployAppName, deploymentName);
43
33
  const app = createQPQApp();
44
34
  // Account-level guardrails - one statically named stack per AWS account,
45
35
  // independent of any app. Exactly one repo/config owns this per account, and
@@ -1 +1 @@
1
- {"version":3,"file":"createWorkspaceQpqCdkApp.js","sourceRoot":"","sources":["../../../src/appWorkspace/createWorkspaceQpqCdkApp.ts"],"names":[],"mappings":"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","sourcesContent":["import * as cdk from 'aws-cdk-lib';\nimport fs from 'fs';\nimport path from 'path';\n\nimport { createQPQApp } from '../QPQApp';\nimport {\n AccountQpqStack,\n ApiQpqServiceStack,\n BootstrapQpqServiceStack,\n DomainQpqServiceStack,\n InfQpqServiceStack,\n WebQpqServiceStack,\n} from '../stacks';\nimport * as qpqDeployAwsCdkUtils from '../utils';\nimport { getWorkspaceAccountQpqConfig, getWorkspaceBootstrapQpqConfig, getWorkspaceServiceQpqConfig } from './getWorkspaceQpqConfigs';\nimport { getQpqAppDeployContext } from './qpqAppDeployConfig';\n\n// Walk up from cwd to the workspace root (the directory with an apps/ folder).\nexport const findWorkspaceRoot = (startDir: string = process.cwd()): string => {\n let dir = startDir;\n while (!fs.existsSync(path.join(dir, 'apps')) || !fs.existsSync(path.join(dir, 'package.json'))) {\n const parent = path.dirname(dir);\n if (parent === dir) throw new Error('Could not find workspace root (a directory containing apps/ and package.json)');\n dir = parent;\n }\n return dir;\n};\n\n// The generic CDK app for a QPQ app workspace — the same stacks for every\n// product; everything app-specific comes from apps/<app>/ (deploy.config.json\n// + optional account.qpq.ts / bootstrap.qpq.ts fragments).\n//\n// Driven by environment variables set by the invoking tool (`qpq go`):\n// DEPLOY_APP_NAME (required) app folder under apps/\n// DEPLOY_SERVICE_NAME (optional) also synth the service's inf/web/api stacks\n// ENVIRONMENT (required) environment name in deploy.config.json\n// ACTOR_NAME (optional) actor/feature deploys\n// AWS_DEFAULT_ACCOUNT / AWS_DEFAULT_REGION (optional) identity override\nexport const createWorkspaceQpqCdkApp = (): cdk.App => {\n const root = findWorkspaceRoot();\n\n const deployAppName = process.env.DEPLOY_APP_NAME;\n if (!deployAppName) {\n throw new Error('DEPLOY_APP_NAME environment variable is not set.');\n }\n\n const environment = process.env.ENVIRONMENT;\n if (!environment) {\n throw new Error('ENVIRONMENT environment variable is not set.');\n }\n\n const ctx = getQpqAppDeployContext(root, deployAppName, environment, process.env.ACTOR_NAME, {\n accountId: process.env.AWS_DEFAULT_ACCOUNT,\n region: process.env.AWS_DEFAULT_REGION,\n });\n\n const app = createQPQApp();\n\n // Account-level guardrails - one statically named stack per AWS account,\n // independent of any app. Exactly one repo/config owns this per account, and\n // actor deploys must not deploy it.\n new AccountQpqStack(app, qpqDeployAwsCdkUtils.getAccountStackName(), {\n qpqConfig: getWorkspaceAccountQpqConfig(ctx),\n });\n\n // Bootstrapping stacks — deploy when their config changes.\n const bootstrapQpqConfig = getWorkspaceBootstrapQpqConfig(ctx);\n\n new DomainQpqServiceStack(app, qpqDeployAwsCdkUtils.getDomainStackName(bootstrapQpqConfig), {\n qpqConfig: bootstrapQpqConfig,\n });\n\n new BootstrapQpqServiceStack(app, qpqDeployAwsCdkUtils.getBootstrapStackName(bootstrapQpqConfig), {\n qpqConfig: bootstrapQpqConfig,\n });\n\n if (process.env.DEPLOY_SERVICE_NAME) {\n const qpqConfig = getWorkspaceServiceQpqConfig(root, deployAppName, process.env.DEPLOY_SERVICE_NAME);\n\n new InfQpqServiceStack(app, qpqDeployAwsCdkUtils.getInfStackName(qpqConfig), {\n qpqConfig,\n });\n new WebQpqServiceStack(app, qpqDeployAwsCdkUtils.getWebStackName(qpqConfig), {\n qpqConfig,\n });\n new ApiQpqServiceStack(app, qpqDeployAwsCdkUtils.getApiStackName(qpqConfig), {\n qpqConfig,\n });\n }\n\n return app;\n};\n"]}
1
+ {"version":3,"file":"createWorkspaceQpqCdkApp.js","sourceRoot":"","sources":["../../../src/appWorkspace/createWorkspaceQpqCdkApp.ts"],"names":[],"mappings":"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","sourcesContent":["import { getQpqAppDeployment, primeAwsDeploymentEnv, primeDeploymentEnv, validateAwsDeployment } from 'quidproquo-config-aws';\nimport { QpqDeployEnvVar } from 'quidproquo-core';\n\nimport * as cdk from 'aws-cdk-lib';\n\nimport { createQPQApp } from '../QPQApp';\nimport {\n AccountQpqStack,\n ApiQpqServiceStack,\n BootstrapQpqServiceStack,\n DomainQpqServiceStack,\n InfQpqServiceStack,\n WebQpqServiceStack,\n} from '../stacks';\nimport * as qpqDeployAwsCdkUtils from '../utils';\nimport { getQpqAppDeployContext } from './getQpqAppDeployContext';\nimport { getWorkspaceAccountQpqConfig, getWorkspaceBootstrapQpqConfig, getWorkspaceServiceQpqConfig } from './getWorkspaceQpqConfigs';\n\n// The generic CDK app for a QPQ app workspace: the same stacks for every\n// product; everything app-specific comes from apps/<app>/ (deploy.config.json\n// + optional account.qpq.ts / bootstrap.qpq.ts fragments).\n//\n// Driven by two env vars set by the invoking tool (`qpq go`):\n// DEPLOY_APP_NAME app folder under apps/\n// DEPLOY_NAME deployment name in that app's deploy.config.json\n// plus DEPLOY_SERVICE_NAME (optional) to also synth a service's inf/web/api\n// stacks. Everything else (identity, environment, feature, settings) comes from\n// the deployment entry, primed here before any service config is required.\nexport const createWorkspaceQpqCdkApp = (): cdk.App => {\n // qpq spawns cdk with cwd at the workspace root.\n const root = process.cwd();\n\n const deployAppName = process.env[QpqDeployEnvVar.deployAppName];\n if (!deployAppName) {\n throw new Error(`${QpqDeployEnvVar.deployAppName} environment variable is not set.`);\n }\n\n const deploymentName = process.env[QpqDeployEnvVar.deployName];\n if (!deploymentName) {\n throw new Error(`${QpqDeployEnvVar.deployName} environment variable is not set.`);\n }\n\n const deployment = validateAwsDeployment(deploymentName, getQpqAppDeployment(root, deployAppName, deploymentName));\n primeDeploymentEnv(deployAppName, deploymentName, deployment);\n primeAwsDeploymentEnv(deployment);\n const ctx = getQpqAppDeployContext(root, deployAppName, deploymentName);\n\n const app = createQPQApp();\n\n // Account-level guardrails - one statically named stack per AWS account,\n // independent of any app. Exactly one repo/config owns this per account, and\n // actor deploys must not deploy it.\n new AccountQpqStack(app, qpqDeployAwsCdkUtils.getAccountStackName(), {\n qpqConfig: getWorkspaceAccountQpqConfig(ctx),\n });\n\n // Bootstrapping stacks — deploy when their config changes.\n const bootstrapQpqConfig = getWorkspaceBootstrapQpqConfig(ctx);\n\n new DomainQpqServiceStack(app, qpqDeployAwsCdkUtils.getDomainStackName(bootstrapQpqConfig), {\n qpqConfig: bootstrapQpqConfig,\n });\n\n new BootstrapQpqServiceStack(app, qpqDeployAwsCdkUtils.getBootstrapStackName(bootstrapQpqConfig), {\n qpqConfig: bootstrapQpqConfig,\n });\n\n if (process.env.DEPLOY_SERVICE_NAME) {\n const qpqConfig = getWorkspaceServiceQpqConfig(root, deployAppName, process.env.DEPLOY_SERVICE_NAME);\n\n new InfQpqServiceStack(app, qpqDeployAwsCdkUtils.getInfStackName(qpqConfig), {\n qpqConfig,\n });\n new WebQpqServiceStack(app, qpqDeployAwsCdkUtils.getWebStackName(qpqConfig), {\n qpqConfig,\n });\n new ApiQpqServiceStack(app, qpqDeployAwsCdkUtils.getApiStackName(qpqConfig), {\n qpqConfig,\n });\n }\n\n return app;\n};\n"]}
@@ -0,0 +1,3 @@
1
+ import { QpqAppDeployContext } from './QpqAppDeployContext';
2
+ /** Builds the context the CDK app hands to config fragments. Throws for a non-AWS deployment, which never reaches CDK. */
3
+ export declare const getQpqAppDeployContext: (root: string, appName: string, deploymentName: string) => QpqAppDeployContext;
@@ -0,0 +1,18 @@
1
+ import { getQpqAppDeployment, validateAwsDeployment } from 'quidproquo-config-aws';
2
+ import path from 'path';
3
+ /** Builds the context the CDK app hands to config fragments. Throws for a non-AWS deployment, which never reaches CDK. */
4
+ export const getQpqAppDeployContext = (root, appName, deploymentName) => {
5
+ const deployment = validateAwsDeployment(deploymentName, getQpqAppDeployment(root, appName, deploymentName));
6
+ return {
7
+ appName,
8
+ appDir: path.join(root, 'apps', appName),
9
+ deploymentName,
10
+ name: deployment.name,
11
+ environment: deployment.environment,
12
+ feature: deployment.feature,
13
+ settings: deployment.settings ?? {},
14
+ accountId: deployment.platformSettings.accountId,
15
+ region: deployment.platformSettings.region,
16
+ };
17
+ };
18
+ //# sourceMappingURL=getQpqAppDeployContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getQpqAppDeployContext.js","sourceRoot":"","sources":["../../../src/appWorkspace/getQpqAppDeployContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEnF,OAAO,IAAI,MAAM,MAAM,CAAC;AAIxB,0HAA0H;AAC1H,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,IAAY,EAAE,OAAe,EAAE,cAAsB,EAAuB,EAAE;IACnH,MAAM,UAAU,GAAG,qBAAqB,CAAC,cAAc,EAAE,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;IAE7G,OAAO;QACL,OAAO;QACP,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;QAExC,cAAc;QAEd,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,QAAQ,EAAE,UAAU,CAAC,QAAQ,IAAI,EAAE;QAEnC,SAAS,EAAE,UAAU,CAAC,gBAAgB,CAAC,SAAS;QAChD,MAAM,EAAE,UAAU,CAAC,gBAAgB,CAAC,MAAM;KACb,CAAC;AAClC,CAAC,CAAC","sourcesContent":["import { getQpqAppDeployment, validateAwsDeployment } from 'quidproquo-config-aws';\n\nimport path from 'path';\n\nimport { QpqAppDeployContext } from './QpqAppDeployContext';\n\n/** Builds the context the CDK app hands to config fragments. Throws for a non-AWS deployment, which never reaches CDK. */\nexport const getQpqAppDeployContext = (root: string, appName: string, deploymentName: string): QpqAppDeployContext => {\n const deployment = validateAwsDeployment(deploymentName, getQpqAppDeployment(root, appName, deploymentName));\n\n return {\n appName,\n appDir: path.join(root, 'apps', appName),\n\n deploymentName,\n\n name: deployment.name,\n environment: deployment.environment,\n feature: deployment.feature,\n settings: deployment.settings ?? {},\n\n accountId: deployment.platformSettings.accountId,\n region: deployment.platformSettings.region,\n } satisfies QpqAppDeployContext;\n};\n"]}
@@ -1,5 +1,5 @@
1
1
  import { QPQConfig } from 'quidproquo-core';
2
- import { QpqAppDeployContext } from './qpqAppDeployConfig';
2
+ import { QpqAppDeployContext } from './QpqAppDeployContext';
3
3
  export declare const getWorkspaceAccountQpqConfig: (ctx: QpqAppDeployContext) => QPQConfig;
4
4
  export declare const getWorkspaceBootstrapQpqConfig: (ctx: QpqAppDeployContext) => QPQConfig;
5
5
  export declare const getWorkspaceServiceQpqConfig: (root: string, appName: string, serviceName: string) => QPQConfig;
@@ -28,10 +28,10 @@ export const getWorkspaceAccountQpqConfig = (ctx) => [
28
28
  ];
29
29
  // The app's bootstrap config: apex/zone/cert/WAF-level resources every service
30
30
  // in the app builds on. The fragment declares defineDns like every service does.
31
- // Resource names are prefixed with the app prefix, so multiple apps can bootstrap
32
- // into one shared AWS account.
31
+ // Resource names are prefixed with the deployment's name, so multiple deployments
32
+ // can bootstrap into one shared AWS account.
33
33
  export const getWorkspaceBootstrapQpqConfig = (ctx) => [
34
- defineApplication(ctx.prefix, ctx.environment, ctx.appDir, ctx.actorName),
34
+ defineApplication(ctx.name, ctx.environment, ctx.appDir, ctx.feature),
35
35
  defineAwsServiceAccountInfo(ctx.accountId, ctx.region),
36
36
  ...loadAppConfigFragment(ctx, 'bootstrap.qpq.ts'),
37
37
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"getWorkspaceQpqConfigs.js","sourceRoot":"","sources":["../../../src/appWorkspace/getWorkspaceQpqConfigs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAa,MAAM,iBAAiB,CAAC;AAE/D,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAIxB,0EAA0E;AAC1E,8EAA8E;AAC9E,qDAAqD;AACrD,8EAA8E;AAC9E,6EAA6E;AAC7E,8DAA8D;AAC9D,MAAM,qBAAqB,GAAG,CAAC,GAAwB,EAAE,QAAgB,EAAa,EAAE;IACtF,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,iEAAiE;IACjE,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACrF,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,IAAI,cAAc,CAAC;IAE1D,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnE,CAAC,CAAC;AAEF,yEAAyE;AACzE,8EAA8E;AAC9E,uDAAuD;AACvD,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,GAAwB,EAAa,EAAE,CAAC;IACnF,iBAAiB,CAAC,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC;IAEzD,2BAA2B,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC;IAEtD,GAAG,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,CAAC;CAChD,CAAC;AAEF,+EAA+E;AAC/E,iFAAiF;AACjF,kFAAkF;AAClF,+BAA+B;AAC/B,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,GAAwB,EAAa,EAAE,CAAC;IACrF,iBAAiB,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC;IAEzE,2BAA2B,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC;IAEtD,GAAG,qBAAqB,CAAC,GAAG,EAAE,kBAAkB,CAAC;CAClD,CAAC;AAEF,8EAA8E;AAC9E,8EAA8E;AAC9E,+DAA+D;AAC/D,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,IAAY,EAAE,OAAe,EAAE,WAAmB,EAAa,EAAE;IAC5G,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC;IAEzH,iEAAiE;IACjE,MAAM,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAChD,OAAO,WAAW,CAAC,OAAO,IAAI,WAAW,CAAC;AAC5C,CAAC,CAAC","sourcesContent":["import { defineAwsServiceAccountInfo } from 'quidproquo-config-aws';\nimport { defineApplication, QPQConfig } from 'quidproquo-core';\n\nimport fs from 'fs';\nimport path from 'path';\n\nimport { QpqAppDeployContext } from './qpqAppDeployConfig';\n\n// Loads an optional per-app config fragment (apps/<app>/account.qpq.ts or\n// bootstrap.qpq.ts) — a default-exported `(ctx) => QPQConfig` of app-specific\n// EXTRAS. The identity plumbing (defineApplication +\n// defineAwsServiceAccountInfo) is provided by the callers below, so fragments\n// must not declare their own. Requiring TS relies on the caller running with\n// TS require hooks (ts-node), as the CDK app invocation does.\nconst loadAppConfigFragment = (ctx: QpqAppDeployContext, fileName: string): QPQConfig => {\n const fragmentPath = path.join(ctx.appDir, fileName);\n if (!fs.existsSync(fragmentPath)) {\n return [];\n }\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fragmentModule = require(path.join(ctx.appDir, fileName.replace(/\\.ts$/, '')));\n const fragment = fragmentModule.default || fragmentModule;\n\n return typeof fragment === 'function' ? fragment(ctx) : fragment;\n};\n\n// Account-level guardrails — one statically named stack per AWS account,\n// independent of any app. The app name / environment here only satisfy config\n// plumbing; account resources key on accountId/region.\nexport const getWorkspaceAccountQpqConfig = (ctx: QpqAppDeployContext): QPQConfig => [\n defineApplication('account', ctx.environment, ctx.appDir),\n\n defineAwsServiceAccountInfo(ctx.accountId, ctx.region),\n\n ...loadAppConfigFragment(ctx, 'account.qpq.ts'),\n];\n\n// The app's bootstrap config: apex/zone/cert/WAF-level resources every service\n// in the app builds on. The fragment declares defineDns like every service does.\n// Resource names are prefixed with the app prefix, so multiple apps can bootstrap\n// into one shared AWS account.\nexport const getWorkspaceBootstrapQpqConfig = (ctx: QpqAppDeployContext): QPQConfig => [\n defineApplication(ctx.prefix, ctx.environment, ctx.appDir, ctx.actorName),\n\n defineAwsServiceAccountInfo(ctx.accountId, ctx.region),\n\n ...loadAppConfigFragment(ctx, 'bootstrap.qpq.ts'),\n];\n\n// A service's own QPQ config, loaded straight from its infrastructure.ts (its\n// @scope imports resolve through the workspace node_modules symlinks to built\n// dist — no tsconfig-paths registration needed under ts-node).\nexport const getWorkspaceServiceQpqConfig = (root: string, appName: string, serviceName: string): QPQConfig => {\n const infrastructurePath = path.join(root, 'apps', appName, 'services', serviceName, 'service', 'src', 'infrastructure');\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const infraModule = require(infrastructurePath);\n return infraModule.default || infraModule;\n};\n"]}
1
+ {"version":3,"file":"getWorkspaceQpqConfigs.js","sourceRoot":"","sources":["../../../src/appWorkspace/getWorkspaceQpqConfigs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAa,MAAM,iBAAiB,CAAC;AAE/D,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAIxB,0EAA0E;AAC1E,8EAA8E;AAC9E,qDAAqD;AACrD,8EAA8E;AAC9E,6EAA6E;AAC7E,8DAA8D;AAC9D,MAAM,qBAAqB,GAAG,CAAC,GAAwB,EAAE,QAAgB,EAAa,EAAE;IACtF,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACrD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,iEAAiE;IACjE,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACrF,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,IAAI,cAAc,CAAC;IAE1D,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnE,CAAC,CAAC;AAEF,yEAAyE;AACzE,8EAA8E;AAC9E,uDAAuD;AACvD,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,GAAwB,EAAa,EAAE,CAAC;IACnF,iBAAiB,CAAC,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC;IAEzD,2BAA2B,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC;IAEtD,GAAG,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,CAAC;CAChD,CAAC;AAEF,+EAA+E;AAC/E,iFAAiF;AACjF,kFAAkF;AAClF,6CAA6C;AAC7C,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,GAAwB,EAAa,EAAE,CAAC;IACrF,iBAAiB,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC;IAErE,2BAA2B,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC;IAEtD,GAAG,qBAAqB,CAAC,GAAG,EAAE,kBAAkB,CAAC;CAClD,CAAC;AAEF,8EAA8E;AAC9E,8EAA8E;AAC9E,+DAA+D;AAC/D,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,IAAY,EAAE,OAAe,EAAE,WAAmB,EAAa,EAAE;IAC5G,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC;IAEzH,iEAAiE;IACjE,MAAM,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAChD,OAAO,WAAW,CAAC,OAAO,IAAI,WAAW,CAAC;AAC5C,CAAC,CAAC","sourcesContent":["import { defineAwsServiceAccountInfo } from 'quidproquo-config-aws';\nimport { defineApplication, QPQConfig } from 'quidproquo-core';\n\nimport fs from 'fs';\nimport path from 'path';\n\nimport { QpqAppDeployContext } from './QpqAppDeployContext';\n\n// Loads an optional per-app config fragment (apps/<app>/account.qpq.ts or\n// bootstrap.qpq.ts) — a default-exported `(ctx) => QPQConfig` of app-specific\n// EXTRAS. The identity plumbing (defineApplication +\n// defineAwsServiceAccountInfo) is provided by the callers below, so fragments\n// must not declare their own. Requiring TS relies on the caller running with\n// TS require hooks (ts-node), as the CDK app invocation does.\nconst loadAppConfigFragment = (ctx: QpqAppDeployContext, fileName: string): QPQConfig => {\n const fragmentPath = path.join(ctx.appDir, fileName);\n if (!fs.existsSync(fragmentPath)) {\n return [];\n }\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fragmentModule = require(path.join(ctx.appDir, fileName.replace(/\\.ts$/, '')));\n const fragment = fragmentModule.default || fragmentModule;\n\n return typeof fragment === 'function' ? fragment(ctx) : fragment;\n};\n\n// Account-level guardrails — one statically named stack per AWS account,\n// independent of any app. The app name / environment here only satisfy config\n// plumbing; account resources key on accountId/region.\nexport const getWorkspaceAccountQpqConfig = (ctx: QpqAppDeployContext): QPQConfig => [\n defineApplication('account', ctx.environment, ctx.appDir),\n\n defineAwsServiceAccountInfo(ctx.accountId, ctx.region),\n\n ...loadAppConfigFragment(ctx, 'account.qpq.ts'),\n];\n\n// The app's bootstrap config: apex/zone/cert/WAF-level resources every service\n// in the app builds on. The fragment declares defineDns like every service does.\n// Resource names are prefixed with the deployment's name, so multiple deployments\n// can bootstrap into one shared AWS account.\nexport const getWorkspaceBootstrapQpqConfig = (ctx: QpqAppDeployContext): QPQConfig => [\n defineApplication(ctx.name, ctx.environment, ctx.appDir, ctx.feature),\n\n defineAwsServiceAccountInfo(ctx.accountId, ctx.region),\n\n ...loadAppConfigFragment(ctx, 'bootstrap.qpq.ts'),\n];\n\n// A service's own QPQ config, loaded straight from its infrastructure.ts (its\n// @scope imports resolve through the workspace node_modules symlinks to built\n// dist — no tsconfig-paths registration needed under ts-node).\nexport const getWorkspaceServiceQpqConfig = (root: string, appName: string, serviceName: string): QPQConfig => {\n const infrastructurePath = path.join(root, 'apps', appName, 'services', serviceName, 'service', 'src', 'infrastructure');\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const infraModule = require(infrastructurePath);\n return infraModule.default || infraModule;\n};\n"]}