@aws-blocks/core 0.1.18 → 0.3.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/dist/cdk/__fixtures__/import-meta-handler.d.ts +6 -0
- package/dist/cdk/__fixtures__/import-meta-handler.d.ts.map +1 -0
- package/dist/cdk/__fixtures__/import-meta-handler.js +13 -0
- package/dist/cdk/blocks-backend.d.ts +46 -9
- package/dist/cdk/blocks-backend.d.ts.map +1 -1
- package/dist/cdk/blocks-backend.js +66 -67
- package/dist/cdk/blocks-backend.test.js +114 -56
- package/dist/cdk/blocks-defaults.d.ts +70 -0
- package/dist/cdk/blocks-defaults.d.ts.map +1 -0
- package/dist/cdk/blocks-defaults.js +27 -0
- package/dist/cdk/blocks-defaults.test.d.ts +2 -0
- package/dist/cdk/blocks-defaults.test.d.ts.map +1 -0
- package/dist/cdk/blocks-defaults.test.js +24 -0
- package/dist/cdk/blocks-stack.test.js +74 -31
- package/dist/cdk/bundling.d.ts +42 -0
- package/dist/cdk/bundling.d.ts.map +1 -0
- package/dist/cdk/bundling.js +72 -0
- package/dist/cdk/bundling.test.d.ts +2 -0
- package/dist/cdk/bundling.test.d.ts.map +1 -0
- package/dist/cdk/bundling.test.js +80 -0
- package/dist/cdk/compute/compute.d.ts +33 -0
- package/dist/cdk/compute/compute.d.ts.map +1 -0
- package/dist/cdk/compute/compute.js +28 -0
- package/dist/cdk/compute/default-compute-factory.d.ts +33 -0
- package/dist/cdk/compute/default-compute-factory.d.ts.map +1 -0
- package/dist/cdk/compute/default-compute-factory.js +3 -0
- package/dist/cdk/index.d.ts +92 -12
- package/dist/cdk/index.d.ts.map +1 -1
- package/dist/cdk/index.js +141 -25
- package/dist/cdk/internal.d.ts +25 -0
- package/dist/cdk/internal.d.ts.map +1 -0
- package/dist/cdk/internal.js +27 -0
- package/dist/cdk/mixins.d.ts +18 -4
- package/dist/cdk/mixins.d.ts.map +1 -1
- package/dist/cdk/mixins.js +31 -7
- package/dist/cdk/mixins.test.js +45 -0
- package/dist/common/index.d.ts +9 -0
- package/dist/common/index.d.ts.map +1 -1
- package/dist/hosting-secrets.d.ts +48 -0
- package/dist/hosting-secrets.d.ts.map +1 -0
- package/dist/hosting-secrets.js +57 -0
- package/dist/hosting-secrets.test.d.ts +2 -0
- package/dist/hosting-secrets.test.d.ts.map +1 -0
- package/dist/hosting-secrets.test.js +81 -0
- package/dist/hosting.d.ts +88 -6
- package/dist/hosting.d.ts.map +1 -1
- package/dist/hosting.js +83 -24
- package/dist/hosting.test.js +158 -10
- package/dist/index.cdk.d.ts +13 -11
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +13 -8
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/lambda-handler.test.js +66 -0
- package/dist/pipeline/index.d.ts +24 -2
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/index.js +41 -1
- package/dist/pipeline.test.d.ts +2 -0
- package/dist/pipeline.test.d.ts.map +1 -0
- package/dist/pipeline.test.js +47 -0
- package/dist/rpc.d.ts +21 -0
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +46 -3
- package/dist/rpc.test.js +58 -1
- package/dist/scripts/config.d.ts +15 -0
- package/dist/scripts/config.d.ts.map +1 -0
- package/dist/scripts/config.js +29 -0
- package/dist/scripts/deploy.d.ts.map +1 -1
- package/dist/scripts/deploy.js +4 -0
- package/dist/scripts/dev-server.d.ts +8 -0
- package/dist/scripts/dev-server.d.ts.map +1 -1
- package/dist/scripts/dev-server.js +31 -0
- package/dist/scripts/generate-client.d.ts.map +1 -1
- package/dist/scripts/generate-client.js +9 -4
- package/dist/scripts/generate-client.test.js +19 -4
- package/dist/scripts/index.d.ts +13 -10
- package/dist/scripts/index.d.ts.map +1 -1
- package/dist/scripts/index.js +11 -8
- package/dist/scripts/preflight-credentials.d.ts +30 -0
- package/dist/scripts/preflight-credentials.d.ts.map +1 -0
- package/dist/scripts/preflight-credentials.js +108 -0
- package/dist/scripts/preflight-credentials.test.d.ts +2 -0
- package/dist/scripts/preflight-credentials.test.d.ts.map +1 -0
- package/dist/scripts/preflight-credentials.test.js +77 -0
- package/dist/scripts/sandbox-args.test.d.ts +2 -0
- package/dist/scripts/sandbox-args.test.d.ts.map +1 -0
- package/dist/scripts/sandbox-args.test.js +77 -0
- package/dist/scripts/sandbox.d.ts +52 -0
- package/dist/scripts/sandbox.d.ts.map +1 -1
- package/dist/scripts/sandbox.js +92 -15
- package/dist/scripts/secret.d.ts +15 -0
- package/dist/scripts/secret.d.ts.map +1 -0
- package/dist/scripts/secret.js +29 -0
- package/dist/scripts/secret.test.d.ts +2 -0
- package/dist/scripts/secret.test.d.ts.map +1 -0
- package/dist/scripts/secret.test.js +28 -0
- package/dist/scripts/telemetry.test.js +2 -0
- package/dist/scripts/typegen.d.ts +15 -0
- package/dist/scripts/typegen.d.ts.map +1 -0
- package/dist/scripts/typegen.js +16 -0
- package/dist/secret-naming.d.ts +58 -0
- package/dist/secret-naming.d.ts.map +1 -0
- package/dist/secret-naming.js +67 -0
- package/dist/secret-naming.test.d.ts +2 -0
- package/dist/secret-naming.test.d.ts.map +1 -0
- package/dist/secret-naming.test.js +40 -0
- package/dist/telemetry/environment.d.ts.map +1 -1
- package/dist/telemetry/environment.js +3 -1
- package/dist/telemetry/telemetry.test.js +10 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -1
- package/src/cdk/__fixtures__/import-meta-handler.ts +16 -0
- package/src/cdk/blocks-backend.test.ts +308 -237
- package/src/cdk/blocks-backend.ts +98 -77
- package/src/cdk/blocks-defaults.test.ts +27 -0
- package/src/cdk/blocks-defaults.ts +75 -0
- package/src/cdk/blocks-stack.test.ts +160 -110
- package/src/cdk/bundling.test.ts +90 -0
- package/src/cdk/bundling.ts +76 -0
- package/src/cdk/compute/compute.ts +37 -0
- package/src/cdk/compute/default-compute-factory.ts +37 -0
- package/src/cdk/index.ts +178 -27
- package/src/cdk/internal.ts +30 -0
- package/src/cdk/mixins.test.ts +56 -1
- package/src/cdk/mixins.ts +32 -7
- package/src/common/index.ts +9 -0
- package/src/hosting-secrets.test.ts +106 -0
- package/src/hosting-secrets.ts +106 -0
- package/src/hosting.test.ts +244 -63
- package/src/hosting.ts +180 -68
- package/src/index.cdk.ts +79 -36
- package/src/index.ts +35 -16
- package/src/lambda-handler.test.ts +74 -0
- package/src/pipeline/index.ts +64 -11
- package/src/pipeline.test.ts +55 -0
- package/src/rpc.test.ts +66 -1
- package/src/rpc.ts +60 -3
- package/src/scripts/config.ts +35 -0
- package/src/scripts/deploy.ts +5 -0
- package/src/scripts/dev-server.ts +40 -0
- package/src/scripts/generate-client.test.ts +43 -8
- package/src/scripts/generate-client.ts +70 -65
- package/src/scripts/index.ts +20 -17
- package/src/scripts/preflight-credentials.test.ts +110 -0
- package/src/scripts/preflight-credentials.ts +131 -0
- package/src/scripts/sandbox-args.test.ts +92 -0
- package/src/scripts/sandbox.ts +102 -15
- package/src/scripts/secret.test.ts +35 -0
- package/src/scripts/secret.ts +35 -0
- package/src/scripts/telemetry.test.ts +2 -0
- package/src/scripts/typegen.ts +18 -0
- package/src/secret-naming.test.ts +59 -0
- package/src/secret-naming.ts +99 -0
- package/src/telemetry/environment.ts +3 -1
- package/src/telemetry/telemetry.test.ts +12 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import assert from 'node:assert';
|
|
4
|
+
import { describe, it, mock } from 'node:test';
|
|
5
|
+
import { assertAwsCredentials, resolveProbeRegion } from './preflight-credentials.js';
|
|
6
|
+
const REGION_ENV = { AWS_REGION: 'us-east-1' };
|
|
7
|
+
/** Build an Error with a specific `name`, as the AWS SDK throws. */
|
|
8
|
+
function namedError(name, message = 'raw sdk detail') {
|
|
9
|
+
const err = new Error(message);
|
|
10
|
+
err.name = name;
|
|
11
|
+
return err;
|
|
12
|
+
}
|
|
13
|
+
describe('resolveProbeRegion', () => {
|
|
14
|
+
it('prefers AWS_REGION', () => {
|
|
15
|
+
assert.strictEqual(resolveProbeRegion({ AWS_REGION: 'eu-west-1', AWS_DEFAULT_REGION: 'us-east-2' }), 'eu-west-1');
|
|
16
|
+
});
|
|
17
|
+
it('falls back to AWS_DEFAULT_REGION', () => {
|
|
18
|
+
assert.strictEqual(resolveProbeRegion({ AWS_DEFAULT_REGION: 'ap-south-1' }), 'ap-south-1');
|
|
19
|
+
});
|
|
20
|
+
it('returns null when neither is set (caller then skips the probe)', () => {
|
|
21
|
+
assert.strictEqual(resolveProbeRegion({}), null);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
describe('assertAwsCredentials', () => {
|
|
25
|
+
it('resolves silently when the credential probe succeeds', async () => {
|
|
26
|
+
let calledRegion;
|
|
27
|
+
await assert.doesNotReject(() => assertAwsCredentials('sandbox', async (region) => {
|
|
28
|
+
calledRegion = region;
|
|
29
|
+
}, REGION_ENV));
|
|
30
|
+
assert.strictEqual(calledRegion, 'us-east-1');
|
|
31
|
+
});
|
|
32
|
+
it('throws actionable guidance on a real credential error, naming the command', async () => {
|
|
33
|
+
await assert.rejects(() => assertAwsCredentials('sandbox', async () => { throw namedError('CredentialsProviderError'); }, REGION_ENV), (err) => {
|
|
34
|
+
assert.match(err.message, /npm run sandbox/);
|
|
35
|
+
assert.match(err.message, /aws configure|AWS_PROFILE|AWS_ACCESS_KEY_ID/);
|
|
36
|
+
assert.match(err.message, /CredentialsProviderError/); // the name, for debugging
|
|
37
|
+
return true;
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
it('treats an expired token as a credential error', async () => {
|
|
41
|
+
await assert.rejects(() => assertAwsCredentials('deploy', async () => { throw namedError('ExpiredToken'); }, REGION_ENV), (err) => {
|
|
42
|
+
assert.match(err.message, /npm run deploy/);
|
|
43
|
+
assert.match(err.message, /aws sso login|AWS_PROFILE/);
|
|
44
|
+
return true;
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
it('does NOT leak the raw error message (ARN / account id)', async () => {
|
|
48
|
+
const arnMessage = 'User: arn:aws:iam::123456789012:user/alice is not authorized to perform sts:GetCallerIdentity';
|
|
49
|
+
await assert.rejects(() => assertAwsCredentials('deploy', async () => { throw namedError('InvalidClientTokenId', arnMessage); }, REGION_ENV), (err) => {
|
|
50
|
+
assert.doesNotMatch(err.message, /arn:aws:iam/);
|
|
51
|
+
assert.doesNotMatch(err.message, /123456789012/);
|
|
52
|
+
return true;
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
it('does not throw on a network/service error — warns and continues', async () => {
|
|
56
|
+
const warn = mock.method(console, 'warn', () => { });
|
|
57
|
+
try {
|
|
58
|
+
await assert.doesNotReject(() => assertAwsCredentials('sandbox', async () => { throw namedError('TimeoutError'); }, REGION_ENV));
|
|
59
|
+
assert.ok(warn.mock.calls.some((c) => String(c.arguments[0]).includes('Could not verify')), 'should warn that credentials could not be verified');
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
warn.mock.restore();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
it('skips the probe (with a warning) when no region is resolvable', async () => {
|
|
66
|
+
const warn = mock.method(console, 'warn', () => { });
|
|
67
|
+
let probed = false;
|
|
68
|
+
try {
|
|
69
|
+
await assertAwsCredentials('sandbox', async () => { probed = true; }, {});
|
|
70
|
+
assert.strictEqual(probed, false, 'probe must not run without a region');
|
|
71
|
+
assert.ok(warn.mock.calls.some((c) => String(c.arguments[0]).includes('Skipping the AWS credential pre-check')), 'should warn that the pre-check was skipped');
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
warn.mock.restore();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox-args.test.d.ts","sourceRoot":"","sources":["../../src/scripts/sandbox-args.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { describe, it } from 'node:test';
|
|
4
|
+
import assert from 'node:assert';
|
|
5
|
+
import { buildSandboxDeployArgs, sandboxFailureRecoveryHint } from './sandbox.js';
|
|
6
|
+
import { buildCdkDeployArgs } from './deploy-stream.js';
|
|
7
|
+
/**
|
|
8
|
+
* Return the value that follows `flag` in `args`, failing with a clear message
|
|
9
|
+
* if the flag is absent. Reading `args[indexOf(flag) + 1]` directly silently
|
|
10
|
+
* reads `args[0]` when the flag is missing (indexOf returns -1), which turns a
|
|
11
|
+
* dropped flag into a confusing value mismatch instead of a "flag missing".
|
|
12
|
+
*
|
|
13
|
+
* NOTE: resolves only the FIRST occurrence of `flag`. `--context` legitimately
|
|
14
|
+
* appears twice in this argv (projectRoot, then sandboxMode=true), so do not
|
|
15
|
+
* use `valueAfter(args, '--context')` expecting the second value — it silently
|
|
16
|
+
* returns the first. Assert repeated flags with `args.includes(...)` instead.
|
|
17
|
+
*/
|
|
18
|
+
function valueAfter(args, flag) {
|
|
19
|
+
const i = args.indexOf(flag);
|
|
20
|
+
assert.ok(i !== -1, `expected ${flag} in: ${args.join(' ')}`);
|
|
21
|
+
return args[i + 1];
|
|
22
|
+
}
|
|
23
|
+
describe('buildSandboxDeployArgs — express mode (sandbox only)', () => {
|
|
24
|
+
const args = buildSandboxDeployArgs({
|
|
25
|
+
outDir: '.blocks-sandbox',
|
|
26
|
+
projectRoot: '/app',
|
|
27
|
+
backendPath: 'aws-blocks/index.ts',
|
|
28
|
+
});
|
|
29
|
+
it('enables CloudFormation Express Mode via --express', () => {
|
|
30
|
+
assert.ok(args.includes('--express'), `expected --express in: ${args.join(' ')}`);
|
|
31
|
+
});
|
|
32
|
+
it('pairs express mode with the direct deployment method', () => {
|
|
33
|
+
assert.strictEqual(valueAfter(args, '--method'), 'direct');
|
|
34
|
+
});
|
|
35
|
+
it('leaves express-mode rollback at its default (off) — no --rollback', () => {
|
|
36
|
+
// Express Mode disables automatic rollback by default; we keep that default
|
|
37
|
+
// for the throwaway sandbox loop. Passing --rollback here would silently
|
|
38
|
+
// give up the speedup.
|
|
39
|
+
assert.ok(!args.includes('--rollback'), `sandbox must not force --rollback: ${args.join(' ')}`);
|
|
40
|
+
});
|
|
41
|
+
it('deploys every stack so Lambda@Edge apps synth cleanly', () => {
|
|
42
|
+
assert.ok(args.includes('--all'), `expected --all in: ${args.join(' ')}`);
|
|
43
|
+
});
|
|
44
|
+
it('keeps the non-interactive deploy contract and sandbox context', () => {
|
|
45
|
+
assert.deepStrictEqual(args.slice(0, 4), ['exec', 'cdk', '--', 'deploy']);
|
|
46
|
+
assert.strictEqual(valueAfter(args, '--require-approval'), 'never');
|
|
47
|
+
assert.strictEqual(valueAfter(args, '--outputs-file'), '.blocks-sandbox/outputs.json');
|
|
48
|
+
assert.ok(args.includes('sandboxMode=true'), `expected sandboxMode=true in: ${args.join(' ')}`);
|
|
49
|
+
assert.strictEqual(valueAfter(args, '--app'), 'npm exec tsx -- -C cdk aws-blocks/index.ts');
|
|
50
|
+
});
|
|
51
|
+
it('interpolates the provided outDir and projectRoot', () => {
|
|
52
|
+
const custom = buildSandboxDeployArgs({ outDir: 'out', projectRoot: '/other', backendPath: 'b.ts' });
|
|
53
|
+
assert.strictEqual(valueAfter(custom, '--outputs-file'), 'out/outputs.json');
|
|
54
|
+
assert.strictEqual(valueAfter(custom, '--context'), 'projectRoot=/other');
|
|
55
|
+
});
|
|
56
|
+
it('does NOT leak express mode into the production deploy path', () => {
|
|
57
|
+
// A production deploy must keep a reviewable change set and full
|
|
58
|
+
// stabilization with rollback: neither `--express` nor `--method direct`
|
|
59
|
+
// (both sandbox-only speedups) may appear in buildCdkDeployArgs.
|
|
60
|
+
const prod = buildCdkDeployArgs({ projectRoot: '/app', outputsFile: '.blocks-sandbox/outputs.json' });
|
|
61
|
+
assert.ok(!prod.includes('--express'), `production deploy must not use --express: ${prod.join(' ')}`);
|
|
62
|
+
assert.ok(!prod.includes('--method'), `production deploy must not use --method: ${prod.join(' ')}`);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('sandboxFailureRecoveryHint — no silent dead-end after a failed express deploy', () => {
|
|
66
|
+
const hint = sandboxFailureRecoveryHint();
|
|
67
|
+
it('points to the repo-native destroy + redeploy recovery', () => {
|
|
68
|
+
assert.match(hint, /npm run sandbox:destroy/);
|
|
69
|
+
assert.match(hint, /npm run sandbox\b/);
|
|
70
|
+
});
|
|
71
|
+
it('covers the manual UPDATE_ROLLBACK_FAILED escape hatch', () => {
|
|
72
|
+
assert.match(hint, /continue-update-rollback/);
|
|
73
|
+
});
|
|
74
|
+
it('explains why (rollback is off under express mode)', () => {
|
|
75
|
+
assert.match(hint, /rollback is off|automatic rollback/i);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -6,6 +6,58 @@ export interface SandboxOptions {
|
|
|
6
6
|
/** Custom dev server command. Defaults to 'npx vite'. For Next.js use 'npx next dev'. */
|
|
7
7
|
devCommand?: string;
|
|
8
8
|
}
|
|
9
|
+
export interface SandboxDeployArgsOptions {
|
|
10
|
+
/** Directory CDK writes stack outputs to (relative to the project root). */
|
|
11
|
+
outDir: string;
|
|
12
|
+
/** Project root passed to synth as `--context projectRoot=…` (usually `process.cwd()`). */
|
|
13
|
+
projectRoot: string;
|
|
14
|
+
/** Backend entry passed to `--app` so CDK synthesizes from the app definition. */
|
|
15
|
+
backendPath: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Build the `npm exec cdk -- deploy …` argv used by the sandbox deploy.
|
|
19
|
+
*
|
|
20
|
+
* Kept pure (no I/O) so the argv contract — in particular the flags below — can
|
|
21
|
+
* be asserted directly, the same way {@link buildCdkDeployArgs} is for the
|
|
22
|
+
* production path.
|
|
23
|
+
*
|
|
24
|
+
* - `--all`: an app that uses Lambda@Edge (e.g. a Next.js route with
|
|
25
|
+
* `export const runtime = 'edge'`) synthesizes a SECOND stack
|
|
26
|
+
* (`edge-lambda-stack-*`, region us-east-1) in addition to the main hosting
|
|
27
|
+
* stack. Without `--all`, CDK refuses with "specify which stacks to use".
|
|
28
|
+
* Deploying every stack in a sandbox app is the intended behavior.
|
|
29
|
+
* - `--method direct`: skip CloudFormation change-set creation and call
|
|
30
|
+
* UpdateStack directly. This is the baseline sandbox deployment method that
|
|
31
|
+
* `--express` builds on.
|
|
32
|
+
* - `--express` (**CloudFormation Express Mode — sandbox only**): the real
|
|
33
|
+
* speedup. CloudFormation reports each stack operation complete as soon as
|
|
34
|
+
* the resource's configuration is applied, WITHOUT waiting for full
|
|
35
|
+
* stabilization. Because it skips stabilization it also disables automatic
|
|
36
|
+
* rollback by default, so a failed deploy can leave the stack in a failed
|
|
37
|
+
* state — acceptable for the throwaway sandbox iteration loop, never for
|
|
38
|
+
* production. We leave rollback at Express Mode's default (off) and do NOT
|
|
39
|
+
* pass `--rollback`. The production deploy path (`deploy.ts`) deliberately
|
|
40
|
+
* uses neither `--express` nor `--method direct`: it keeps a reviewable
|
|
41
|
+
* change set and full stabilization with rollback, which are exactly the
|
|
42
|
+
* safety signals a production deploy should keep. Requires an aws-cdk CLI
|
|
43
|
+
* new enough to expose `--express`; our pinned `^2.1138.0` has it.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildSandboxDeployArgs({ outDir, projectRoot, backendPath }: SandboxDeployArgsOptions): string[];
|
|
46
|
+
/**
|
|
47
|
+
* Operator-facing recovery guidance printed when a sandbox deploy fails.
|
|
48
|
+
*
|
|
49
|
+
* Express Mode (see {@link buildSandboxDeployArgs}) disables CloudFormation's
|
|
50
|
+
* automatic rollback, so a failed deploy does NOT clean up after itself: a
|
|
51
|
+
* failed *first* deploy leaves the stack in `CREATE_FAILED`/`ROLLBACK_COMPLETE`
|
|
52
|
+
* and a failed update can land in `UPDATE_ROLLBACK_FAILED`. In those states
|
|
53
|
+
* CloudFormation refuses the next `UpdateStack`, so a plain re-run of
|
|
54
|
+
* `npm run sandbox` would hit the same wall — a silent dead-end for the fast
|
|
55
|
+
* loop. This spells out the exact way out instead of leaving the operator to
|
|
56
|
+
* discover it via a cryptic CloudFormation error.
|
|
57
|
+
*
|
|
58
|
+
* Kept pure so it can be asserted in a unit test.
|
|
59
|
+
*/
|
|
60
|
+
export declare function sandboxFailureRecoveryHint(): string;
|
|
9
61
|
export declare function startSandbox(options: SandboxOptions): Promise<string | undefined>;
|
|
10
62
|
export declare function destroySandbox(backendPath: string): Promise<void>;
|
|
11
63
|
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/scripts/sandbox.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/scripts/sandbox.ts"],"names":[],"mappings":"AAiCA,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,2FAA2F;IAC3F,WAAW,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,EAAE,wBAAwB,GAAG,MAAM,EAAE,CAY/G;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAYnD;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,cAAc,+BAwMzD;AAED,wBAAsB,cAAc,CAAC,WAAW,EAAE,MAAM,iBAwCvD"}
|
package/dist/scripts/sandbox.js
CHANGED
|
@@ -5,9 +5,11 @@ import { writeFileSync, mkdirSync, readFileSync } from "node:fs";
|
|
|
5
5
|
import { join, resolve, dirname } from "node:path";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
7
|
import { ensureSecrets, loadEnvFile } from './ensure-secrets.js';
|
|
8
|
+
import { assertAwsCredentials } from './preflight-credentials.js';
|
|
8
9
|
import { applyExternalMigrations } from './external-migrations-step.js';
|
|
9
10
|
import { trackCommand } from '../telemetry/trackCommand.js';
|
|
10
11
|
import { buildAndSendEvent } from '../telemetry/client.js';
|
|
12
|
+
import { classifyError } from '../telemetry/trackCommand.js';
|
|
11
13
|
import { getCdkTelemetryEnv } from './cdk-telemetry-env.js';
|
|
12
14
|
import { runSync, spawnCommand } from './run-command.js';
|
|
13
15
|
import { terminateProcessTree } from './process-tree.js';
|
|
@@ -27,6 +29,74 @@ async function importBackendForRegistry(backendPath) {
|
|
|
27
29
|
// best-effort — telemetry never affects the command
|
|
28
30
|
}
|
|
29
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Build the `npm exec cdk -- deploy …` argv used by the sandbox deploy.
|
|
34
|
+
*
|
|
35
|
+
* Kept pure (no I/O) so the argv contract — in particular the flags below — can
|
|
36
|
+
* be asserted directly, the same way {@link buildCdkDeployArgs} is for the
|
|
37
|
+
* production path.
|
|
38
|
+
*
|
|
39
|
+
* - `--all`: an app that uses Lambda@Edge (e.g. a Next.js route with
|
|
40
|
+
* `export const runtime = 'edge'`) synthesizes a SECOND stack
|
|
41
|
+
* (`edge-lambda-stack-*`, region us-east-1) in addition to the main hosting
|
|
42
|
+
* stack. Without `--all`, CDK refuses with "specify which stacks to use".
|
|
43
|
+
* Deploying every stack in a sandbox app is the intended behavior.
|
|
44
|
+
* - `--method direct`: skip CloudFormation change-set creation and call
|
|
45
|
+
* UpdateStack directly. This is the baseline sandbox deployment method that
|
|
46
|
+
* `--express` builds on.
|
|
47
|
+
* - `--express` (**CloudFormation Express Mode — sandbox only**): the real
|
|
48
|
+
* speedup. CloudFormation reports each stack operation complete as soon as
|
|
49
|
+
* the resource's configuration is applied, WITHOUT waiting for full
|
|
50
|
+
* stabilization. Because it skips stabilization it also disables automatic
|
|
51
|
+
* rollback by default, so a failed deploy can leave the stack in a failed
|
|
52
|
+
* state — acceptable for the throwaway sandbox iteration loop, never for
|
|
53
|
+
* production. We leave rollback at Express Mode's default (off) and do NOT
|
|
54
|
+
* pass `--rollback`. The production deploy path (`deploy.ts`) deliberately
|
|
55
|
+
* uses neither `--express` nor `--method direct`: it keeps a reviewable
|
|
56
|
+
* change set and full stabilization with rollback, which are exactly the
|
|
57
|
+
* safety signals a production deploy should keep. Requires an aws-cdk CLI
|
|
58
|
+
* new enough to expose `--express`; our pinned `^2.1138.0` has it.
|
|
59
|
+
*/
|
|
60
|
+
export function buildSandboxDeployArgs({ outDir, projectRoot, backendPath }) {
|
|
61
|
+
return [
|
|
62
|
+
"exec", "cdk", "--", "deploy",
|
|
63
|
+
"--all",
|
|
64
|
+
"--method", "direct",
|
|
65
|
+
"--express",
|
|
66
|
+
"--require-approval", "never",
|
|
67
|
+
"--outputs-file", `${outDir}/outputs.json`,
|
|
68
|
+
"--context", `projectRoot=${projectRoot}`,
|
|
69
|
+
"--context", "sandboxMode=true",
|
|
70
|
+
"--app", `npm exec tsx -- -C cdk ${backendPath}`,
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Operator-facing recovery guidance printed when a sandbox deploy fails.
|
|
75
|
+
*
|
|
76
|
+
* Express Mode (see {@link buildSandboxDeployArgs}) disables CloudFormation's
|
|
77
|
+
* automatic rollback, so a failed deploy does NOT clean up after itself: a
|
|
78
|
+
* failed *first* deploy leaves the stack in `CREATE_FAILED`/`ROLLBACK_COMPLETE`
|
|
79
|
+
* and a failed update can land in `UPDATE_ROLLBACK_FAILED`. In those states
|
|
80
|
+
* CloudFormation refuses the next `UpdateStack`, so a plain re-run of
|
|
81
|
+
* `npm run sandbox` would hit the same wall — a silent dead-end for the fast
|
|
82
|
+
* loop. This spells out the exact way out instead of leaving the operator to
|
|
83
|
+
* discover it via a cryptic CloudFormation error.
|
|
84
|
+
*
|
|
85
|
+
* Kept pure so it can be asserted in a unit test.
|
|
86
|
+
*/
|
|
87
|
+
export function sandboxFailureRecoveryHint() {
|
|
88
|
+
return [
|
|
89
|
+
"",
|
|
90
|
+
"ℹ️ Express Mode leaves failed sandbox stacks in place (automatic rollback is off).",
|
|
91
|
+
" If the next `npm run sandbox` reports the stack cannot be updated, recover with:",
|
|
92
|
+
"",
|
|
93
|
+
" npm run sandbox:destroy # tears the failed stack down (handles ROLLBACK_COMPLETE)",
|
|
94
|
+
" npm run sandbox # redeploy from a clean slate",
|
|
95
|
+
"",
|
|
96
|
+
" If destroy reports UPDATE_ROLLBACK_FAILED, first run:",
|
|
97
|
+
" aws cloudformation continue-update-rollback --stack-name <your-sandbox-stack>",
|
|
98
|
+
].join("\n");
|
|
99
|
+
}
|
|
30
100
|
export async function startSandbox(options) {
|
|
31
101
|
const { backendPath, outDir = ".blocks-sandbox", clientPort = 3000, deployOnly = false, devCommand } = options;
|
|
32
102
|
const sandboxStartTime = Date.now();
|
|
@@ -39,6 +109,23 @@ export async function startSandbox(options) {
|
|
|
39
109
|
throw e;
|
|
40
110
|
}
|
|
41
111
|
process.env.BLOCKS_STAGE = 'sandbox';
|
|
112
|
+
// Fail fast if AWS credentials are missing/expired, before spending ~10s in
|
|
113
|
+
// synth only to hit an opaque CDK credential error. Unlike deploy(), startSandbox
|
|
114
|
+
// isn't wrapped in trackCommand, so emit the FAIL event manually (mirroring the
|
|
115
|
+
// CDK-deploy failure path below) before rethrowing — otherwise a no-creds abort
|
|
116
|
+
// sends no telemetry.
|
|
117
|
+
try {
|
|
118
|
+
await assertAwsCredentials('sandbox');
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
buildAndSendEvent({
|
|
122
|
+
command: 'sandbox',
|
|
123
|
+
state: 'FAIL',
|
|
124
|
+
duration: Date.now() - sandboxStartTime,
|
|
125
|
+
error: classifyError(error),
|
|
126
|
+
});
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
42
129
|
// Provision connection string to SSM SecureString.
|
|
43
130
|
// On first deploy, creates the parameter. On subsequent deploys, updates if changed.
|
|
44
131
|
// projectRoot is process.cwd() — the same value passed to cdk as --context
|
|
@@ -59,21 +146,10 @@ export async function startSandbox(options) {
|
|
|
59
146
|
console.log("🚀 Deploying to AWS...");
|
|
60
147
|
console.log(" (This may take a few minutes on first deploy)");
|
|
61
148
|
try {
|
|
62
|
-
runSync("npm",
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// (`edge-lambda-stack-*`, region us-east-1) in addition to the main
|
|
67
|
-
// hosting stack. Without `--all`, CDK refuses with "specify which
|
|
68
|
-
// stacks to use". Deploying every stack in a sandbox app is the
|
|
69
|
-
// intended behavior, so select them all.
|
|
70
|
-
"--all",
|
|
71
|
-
"--require-approval", "never",
|
|
72
|
-
"--outputs-file", `${outDir}/outputs.json`,
|
|
73
|
-
"--context", `projectRoot=${process.cwd()}`,
|
|
74
|
-
"--context", "sandboxMode=true",
|
|
75
|
-
"--app", `npm exec tsx -- -C cdk ${backendPath}`,
|
|
76
|
-
], {
|
|
149
|
+
runSync("npm",
|
|
150
|
+
// Argv (including sandbox-only CloudFormation Express Mode, `--express`)
|
|
151
|
+
// is built by buildSandboxDeployArgs — see its doc for why each flag exists.
|
|
152
|
+
buildSandboxDeployArgs({ outDir, projectRoot: process.cwd(), backendPath }), {
|
|
77
153
|
stdio: "inherit",
|
|
78
154
|
env: { ...process.env, NODE_OPTIONS: "--conditions=cdk", ...getCdkTelemetryEnv('sandbox') },
|
|
79
155
|
});
|
|
@@ -86,6 +162,7 @@ export async function startSandbox(options) {
|
|
|
86
162
|
error: { code: 'CDK_DEPLOY_FAILED', phase: 'deploy' },
|
|
87
163
|
});
|
|
88
164
|
console.error("\n❌ Deployment failed.");
|
|
165
|
+
console.error(sandboxFailureRecoveryHint());
|
|
89
166
|
throw error;
|
|
90
167
|
}
|
|
91
168
|
const outputs = JSON.parse(readFileSync(`${outDir}/outputs.json`, "utf-8"));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Set (create or overwrite) a Blocks secret. */
|
|
2
|
+
export declare function setSecret(key: string, value: string, opts?: {
|
|
3
|
+
stage?: string;
|
|
4
|
+
}): Promise<void>;
|
|
5
|
+
/** List Blocks secret keys (names only). */
|
|
6
|
+
export declare function listSecrets(opts?: {
|
|
7
|
+
stage?: string;
|
|
8
|
+
}): Promise<string[]>;
|
|
9
|
+
/** Remove a Blocks secret. */
|
|
10
|
+
export declare function removeSecret(key: string, opts?: {
|
|
11
|
+
stage?: string;
|
|
12
|
+
}): Promise<boolean>;
|
|
13
|
+
/** CLI dispatcher for `blocks secret <set|list|remove> …`. */
|
|
14
|
+
export declare function runSecretCli(argv: string[]): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=secret.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.d.ts","sourceRoot":"","sources":["../../src/scripts/secret.ts"],"names":[],"mappings":"AAgBA,iDAAiD;AACjD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAElG;AAED,4CAA4C;AAC5C,wBAAgB,WAAW,CAAC,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAE5E;AAED,8BAA8B;AAC9B,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAEzF;AAED,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* `blocks secret` — manage a Blocks app's **secrets** (AWS Secrets Manager),
|
|
5
|
+
* pinned to the Blocks `/blocks/<stackId>/secrets` namespace (scoped to the app's
|
|
6
|
+
* stable `stackId` from `.blocks/config.json`, so two apps in one account never
|
|
7
|
+
* collide). Thin wrapper over the shared `@aws-blocks/hosting` CLI with the kind
|
|
8
|
+
* fixed to `secret`. The `config` counterpart is `./config.ts`.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { listValues, removeValue, runValueCli, setValue } from '@aws-blocks/hosting/scripts';
|
|
13
|
+
import { blocksSecretPrefix } from '../secret-naming.js';
|
|
14
|
+
/** Set (create or overwrite) a Blocks secret. */
|
|
15
|
+
export function setSecret(key, value, opts = {}) {
|
|
16
|
+
return setValue('secret', key, value, { prefix: blocksSecretPrefix(), ...opts });
|
|
17
|
+
}
|
|
18
|
+
/** List Blocks secret keys (names only). */
|
|
19
|
+
export function listSecrets(opts = {}) {
|
|
20
|
+
return listValues('secret', { prefix: blocksSecretPrefix(), ...opts });
|
|
21
|
+
}
|
|
22
|
+
/** Remove a Blocks secret. */
|
|
23
|
+
export function removeSecret(key, opts = {}) {
|
|
24
|
+
return removeValue('secret', key, { prefix: blocksSecretPrefix(), ...opts });
|
|
25
|
+
}
|
|
26
|
+
/** CLI dispatcher for `blocks secret <set|list|remove> …`. */
|
|
27
|
+
export function runSecretCli(argv) {
|
|
28
|
+
return runValueCli(argv, { kind: 'secret', prefix: blocksSecretPrefix(), label: 'blocks secret' });
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.test.d.ts","sourceRoot":"","sources":["../../src/scripts/secret.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import assert from 'node:assert';
|
|
4
|
+
import { describe, it } from 'node:test';
|
|
5
|
+
import { runConfigCli } from './config.js';
|
|
6
|
+
import { runSecretCli } from './secret.js';
|
|
7
|
+
// argv parsing + validation only (no live SSM/Secrets Manager). The set/list/
|
|
8
|
+
// remove SDK calls are exercised end-to-end during deploy verification.
|
|
9
|
+
void describe('blocks secret / config CLI argv parsing', () => {
|
|
10
|
+
void it('rejects an unknown subcommand', async () => {
|
|
11
|
+
await assert.rejects(runSecretCli(['frobnicate']), /Unknown subcommand/);
|
|
12
|
+
await assert.rejects(runConfigCli(['frobnicate']), /Unknown subcommand/);
|
|
13
|
+
});
|
|
14
|
+
void it('requires a key for set (label reflects the kind)', async () => {
|
|
15
|
+
await assert.rejects(runSecretCli(['set']), /Usage: blocks secret set/);
|
|
16
|
+
await assert.rejects(runConfigCli(['set']), /Usage: blocks config set/);
|
|
17
|
+
});
|
|
18
|
+
void it('key-only set falls through to the hidden prompt (TTY error, not usage)', async () => {
|
|
19
|
+
await assert.rejects(runSecretCli(['set', 'ONLY_KEY']), /stdin is not a TTY/);
|
|
20
|
+
});
|
|
21
|
+
void it('requires a key for remove', async () => {
|
|
22
|
+
await assert.rejects(runSecretCli(['remove']), /Usage: blocks secret remove/);
|
|
23
|
+
await assert.rejects(runConfigCli(['remove']), /Usage: blocks config remove/);
|
|
24
|
+
});
|
|
25
|
+
void it('validates the key before any store call', async () => {
|
|
26
|
+
await assert.rejects(runSecretCli(['set', '1bad', 'value']), /Invalid key/);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `blocks typegen` — generate the type-safe key augmentation for `getSecret` /
|
|
3
|
+
* `getConfig` in a Blocks app. Statically scans the app's `secret('...')` /
|
|
4
|
+
* `config('...')` calls (no execution, no AWS credentials) and writes a `.d.ts`
|
|
5
|
+
* that narrows the runtime getters to your declared keys (autocomplete + typo
|
|
6
|
+
* errors) with no call-site change.
|
|
7
|
+
*
|
|
8
|
+
* Thin pass-through to the `@aws-blocks/hosting` engine — the getters live on
|
|
9
|
+
* `@aws-blocks/hosting`, so the generated file augments that module by default;
|
|
10
|
+
* a Blocks app needs no extra configuration.
|
|
11
|
+
*
|
|
12
|
+
* @module
|
|
13
|
+
*/
|
|
14
|
+
export { runTypegenCli } from '@aws-blocks/hosting/scripts';
|
|
15
|
+
//# sourceMappingURL=typegen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typegen.d.ts","sourceRoot":"","sources":["../../src/scripts/typegen.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* `blocks typegen` — generate the type-safe key augmentation for `getSecret` /
|
|
5
|
+
* `getConfig` in a Blocks app. Statically scans the app's `secret('...')` /
|
|
6
|
+
* `config('...')` calls (no execution, no AWS credentials) and writes a `.d.ts`
|
|
7
|
+
* that narrows the runtime getters to your declared keys (autocomplete + typo
|
|
8
|
+
* errors) with no call-site change.
|
|
9
|
+
*
|
|
10
|
+
* Thin pass-through to the `@aws-blocks/hosting` engine — the getters live on
|
|
11
|
+
* `@aws-blocks/hosting`, so the generated file augments that module by default;
|
|
12
|
+
* a Blocks app needs no extra configuration.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
export { runTypegenCli } from '@aws-blocks/hosting/scripts';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blocks-owned namespaces for hosting values.
|
|
3
|
+
*
|
|
4
|
+
* The mechanism (markers, resolvers, path/env naming) lives in the
|
|
5
|
+
* framework-neutral `@aws-blocks/hosting` package, which defaults to neutral
|
|
6
|
+
* `/hosting/secrets` and `/hosting/config` prefixes so a non-Blocks consumer
|
|
7
|
+
* never inherits Blocks branding.
|
|
8
|
+
*
|
|
9
|
+
* This module is the ONE place Blocks pins its own namespaces. The prefixes are
|
|
10
|
+
* **scoped to the app's stable `stackId`** (from the committed `.blocks/config.json`)
|
|
11
|
+
* — `/blocks/<stackId>/secrets` (Secrets Manager) and `/blocks/<stackId>/config`
|
|
12
|
+
* (SSM Parameter Store) — so two Blocks apps in one account/region never collide.
|
|
13
|
+
*
|
|
14
|
+
* Both sides derive the scope from the SAME source: the out-of-band CLI
|
|
15
|
+
* (`npm run secret`/`config`) and the CDK synth both call {@link blocksStoreConfig},
|
|
16
|
+
* which reads `stackId` via {@link getStackId}. When `.blocks/config.json` is absent
|
|
17
|
+
* (e.g. a bare unit test) both sides fall back to the unscoped `/blocks/secrets` /
|
|
18
|
+
* `/blocks/config` identically — so the CLI write and the deploy read can never
|
|
19
|
+
* diverge, whether the id is present or not. `stackId` is stage-independent
|
|
20
|
+
* (prod and sandbox share it); use the opt-in `stage` segment for per-stage values.
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
import type { StoreConfig } from '@aws-blocks/hosting/constructs';
|
|
25
|
+
/** Base Blocks prefix for `secret()` values (Secrets Manager); scoped by `stackId`. */
|
|
26
|
+
export declare const BLOCKS_SECRET_PARAMETER_PREFIX = "/blocks/secrets";
|
|
27
|
+
/** Base Blocks prefix for `config()` values (SSM Parameter Store); scoped by `stackId`. */
|
|
28
|
+
export declare const BLOCKS_CONFIG_PARAMETER_PREFIX = "/blocks/config";
|
|
29
|
+
/** The Blocks `secret()` prefix, scoped to `stackId` when available. */
|
|
30
|
+
export declare function blocksSecretPrefix(stackId?: string): string;
|
|
31
|
+
/** The Blocks `config()` prefix, scoped to `stackId` when available. */
|
|
32
|
+
export declare function blocksConfigPrefix(stackId?: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* The **actual store name** of a Blocks `secret()` key in AWS Secrets Manager —
|
|
35
|
+
* routed through {@link secretStoreLocator} so it matches exactly what the CLI
|
|
36
|
+
* writes, the IAM grant scopes to, and the runtime resolver reads. Secrets Manager
|
|
37
|
+
* names are slash-free at the root, so this is `blocks/<stackId>/secrets/<KEY>`
|
|
38
|
+
* (no leading slash), NOT an SSM-style path. Pass `stackId` to compute it without
|
|
39
|
+
* reading `.blocks/config.json`.
|
|
40
|
+
*
|
|
41
|
+
* @example 'blocks/myapp/secrets/STRIPE_KEY'
|
|
42
|
+
*/
|
|
43
|
+
export declare function blocksSecretParameterName(key: string, stackId?: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* The **actual store name** of a Blocks `config()` key in SSM Parameter Store —
|
|
46
|
+
* routed through {@link secretStoreLocator} for consistency with the CLI/grant/
|
|
47
|
+
* runtime path. SSM keeps the leading-slash path.
|
|
48
|
+
*
|
|
49
|
+
* @example '/blocks/myapp/config/FEATURE_FLAGS'
|
|
50
|
+
*/
|
|
51
|
+
export declare function blocksConfigParameterName(key: string, stackId?: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* The Blocks store config passed to the shared hosting engine — `stackId`-scoped
|
|
54
|
+
* prefixes (falling back to the unscoped base when `.blocks/config.json` is absent).
|
|
55
|
+
* Both the CLI and the CDK synth call this, so they resolve identical names.
|
|
56
|
+
*/
|
|
57
|
+
export declare function blocksStoreConfig(stackId?: string): StoreConfig;
|
|
58
|
+
//# sourceMappingURL=secret-naming.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-naming.d.ts","sourceRoot":"","sources":["../src/secret-naming.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAIlE,uFAAuF;AACvF,eAAO,MAAM,8BAA8B,oBAAoB,CAAC;AAEhE,2FAA2F;AAC3F,eAAO,MAAM,8BAA8B,mBAAmB,CAAC;AAgB/D,wEAAwE;AACxE,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,wEAAwE;AACxE,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAK/D"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { secretStoreLocator } from '@aws-blocks/hosting';
|
|
4
|
+
import { getStackId } from './scripts/stack-id.js';
|
|
5
|
+
/** Base Blocks prefix for `secret()` values (Secrets Manager); scoped by `stackId`. */
|
|
6
|
+
export const BLOCKS_SECRET_PARAMETER_PREFIX = '/blocks/secrets';
|
|
7
|
+
/** Base Blocks prefix for `config()` values (SSM Parameter Store); scoped by `stackId`. */
|
|
8
|
+
export const BLOCKS_CONFIG_PARAMETER_PREFIX = '/blocks/config';
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the app's `stackId` (stable, stage-independent, from `.blocks/config.json`),
|
|
11
|
+
* or `undefined` when it can't be read. Never throws — a missing id means both the
|
|
12
|
+
* CLI and synth fall back to the unscoped base prefix, so they still agree.
|
|
13
|
+
*/
|
|
14
|
+
function resolveStackId(explicit) {
|
|
15
|
+
if (explicit)
|
|
16
|
+
return explicit;
|
|
17
|
+
try {
|
|
18
|
+
return getStackId();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** The Blocks `secret()` prefix, scoped to `stackId` when available. */
|
|
25
|
+
export function blocksSecretPrefix(stackId) {
|
|
26
|
+
const id = resolveStackId(stackId);
|
|
27
|
+
return id ? `/blocks/${id}/secrets` : BLOCKS_SECRET_PARAMETER_PREFIX;
|
|
28
|
+
}
|
|
29
|
+
/** The Blocks `config()` prefix, scoped to `stackId` when available. */
|
|
30
|
+
export function blocksConfigPrefix(stackId) {
|
|
31
|
+
const id = resolveStackId(stackId);
|
|
32
|
+
return id ? `/blocks/${id}/config` : BLOCKS_CONFIG_PARAMETER_PREFIX;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The **actual store name** of a Blocks `secret()` key in AWS Secrets Manager —
|
|
36
|
+
* routed through {@link secretStoreLocator} so it matches exactly what the CLI
|
|
37
|
+
* writes, the IAM grant scopes to, and the runtime resolver reads. Secrets Manager
|
|
38
|
+
* names are slash-free at the root, so this is `blocks/<stackId>/secrets/<KEY>`
|
|
39
|
+
* (no leading slash), NOT an SSM-style path. Pass `stackId` to compute it without
|
|
40
|
+
* reading `.blocks/config.json`.
|
|
41
|
+
*
|
|
42
|
+
* @example 'blocks/myapp/secrets/STRIPE_KEY'
|
|
43
|
+
*/
|
|
44
|
+
export function blocksSecretParameterName(key, stackId) {
|
|
45
|
+
return secretStoreLocator(key, { prefix: blocksSecretPrefix(stackId), store: 'secrets-manager' });
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The **actual store name** of a Blocks `config()` key in SSM Parameter Store —
|
|
49
|
+
* routed through {@link secretStoreLocator} for consistency with the CLI/grant/
|
|
50
|
+
* runtime path. SSM keeps the leading-slash path.
|
|
51
|
+
*
|
|
52
|
+
* @example '/blocks/myapp/config/FEATURE_FLAGS'
|
|
53
|
+
*/
|
|
54
|
+
export function blocksConfigParameterName(key, stackId) {
|
|
55
|
+
return secretStoreLocator(key, { prefix: blocksConfigPrefix(stackId), store: 'ssm' });
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The Blocks store config passed to the shared hosting engine — `stackId`-scoped
|
|
59
|
+
* prefixes (falling back to the unscoped base when `.blocks/config.json` is absent).
|
|
60
|
+
* Both the CLI and the CDK synth call this, so they resolve identical names.
|
|
61
|
+
*/
|
|
62
|
+
export function blocksStoreConfig(stackId) {
|
|
63
|
+
return {
|
|
64
|
+
secretStore: { prefix: blocksSecretPrefix(stackId) },
|
|
65
|
+
configStore: { prefix: blocksConfigPrefix(stackId) },
|
|
66
|
+
};
|
|
67
|
+
}
|