@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.
Files changed (159) hide show
  1. package/dist/cdk/__fixtures__/import-meta-handler.d.ts +6 -0
  2. package/dist/cdk/__fixtures__/import-meta-handler.d.ts.map +1 -0
  3. package/dist/cdk/__fixtures__/import-meta-handler.js +13 -0
  4. package/dist/cdk/blocks-backend.d.ts +46 -9
  5. package/dist/cdk/blocks-backend.d.ts.map +1 -1
  6. package/dist/cdk/blocks-backend.js +66 -67
  7. package/dist/cdk/blocks-backend.test.js +114 -56
  8. package/dist/cdk/blocks-defaults.d.ts +70 -0
  9. package/dist/cdk/blocks-defaults.d.ts.map +1 -0
  10. package/dist/cdk/blocks-defaults.js +27 -0
  11. package/dist/cdk/blocks-defaults.test.d.ts +2 -0
  12. package/dist/cdk/blocks-defaults.test.d.ts.map +1 -0
  13. package/dist/cdk/blocks-defaults.test.js +24 -0
  14. package/dist/cdk/blocks-stack.test.js +74 -31
  15. package/dist/cdk/bundling.d.ts +42 -0
  16. package/dist/cdk/bundling.d.ts.map +1 -0
  17. package/dist/cdk/bundling.js +72 -0
  18. package/dist/cdk/bundling.test.d.ts +2 -0
  19. package/dist/cdk/bundling.test.d.ts.map +1 -0
  20. package/dist/cdk/bundling.test.js +80 -0
  21. package/dist/cdk/compute/compute.d.ts +33 -0
  22. package/dist/cdk/compute/compute.d.ts.map +1 -0
  23. package/dist/cdk/compute/compute.js +28 -0
  24. package/dist/cdk/compute/default-compute-factory.d.ts +33 -0
  25. package/dist/cdk/compute/default-compute-factory.d.ts.map +1 -0
  26. package/dist/cdk/compute/default-compute-factory.js +3 -0
  27. package/dist/cdk/index.d.ts +92 -12
  28. package/dist/cdk/index.d.ts.map +1 -1
  29. package/dist/cdk/index.js +141 -25
  30. package/dist/cdk/internal.d.ts +25 -0
  31. package/dist/cdk/internal.d.ts.map +1 -0
  32. package/dist/cdk/internal.js +27 -0
  33. package/dist/cdk/mixins.d.ts +18 -4
  34. package/dist/cdk/mixins.d.ts.map +1 -1
  35. package/dist/cdk/mixins.js +31 -7
  36. package/dist/cdk/mixins.test.js +45 -0
  37. package/dist/common/index.d.ts +9 -0
  38. package/dist/common/index.d.ts.map +1 -1
  39. package/dist/hosting-secrets.d.ts +48 -0
  40. package/dist/hosting-secrets.d.ts.map +1 -0
  41. package/dist/hosting-secrets.js +57 -0
  42. package/dist/hosting-secrets.test.d.ts +2 -0
  43. package/dist/hosting-secrets.test.d.ts.map +1 -0
  44. package/dist/hosting-secrets.test.js +81 -0
  45. package/dist/hosting.d.ts +88 -6
  46. package/dist/hosting.d.ts.map +1 -1
  47. package/dist/hosting.js +83 -24
  48. package/dist/hosting.test.js +158 -10
  49. package/dist/index.cdk.d.ts +13 -11
  50. package/dist/index.cdk.d.ts.map +1 -1
  51. package/dist/index.cdk.js +13 -8
  52. package/dist/index.d.ts +8 -7
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +9 -5
  55. package/dist/lambda-handler.test.js +66 -0
  56. package/dist/pipeline/index.d.ts +24 -2
  57. package/dist/pipeline/index.d.ts.map +1 -1
  58. package/dist/pipeline/index.js +41 -1
  59. package/dist/pipeline.test.d.ts +2 -0
  60. package/dist/pipeline.test.d.ts.map +1 -0
  61. package/dist/pipeline.test.js +47 -0
  62. package/dist/rpc.d.ts +21 -0
  63. package/dist/rpc.d.ts.map +1 -1
  64. package/dist/rpc.js +46 -3
  65. package/dist/rpc.test.js +58 -1
  66. package/dist/scripts/config.d.ts +15 -0
  67. package/dist/scripts/config.d.ts.map +1 -0
  68. package/dist/scripts/config.js +29 -0
  69. package/dist/scripts/deploy.d.ts.map +1 -1
  70. package/dist/scripts/deploy.js +4 -0
  71. package/dist/scripts/dev-server.d.ts +8 -0
  72. package/dist/scripts/dev-server.d.ts.map +1 -1
  73. package/dist/scripts/dev-server.js +31 -0
  74. package/dist/scripts/generate-client.d.ts.map +1 -1
  75. package/dist/scripts/generate-client.js +9 -4
  76. package/dist/scripts/generate-client.test.js +19 -4
  77. package/dist/scripts/index.d.ts +13 -10
  78. package/dist/scripts/index.d.ts.map +1 -1
  79. package/dist/scripts/index.js +11 -8
  80. package/dist/scripts/preflight-credentials.d.ts +30 -0
  81. package/dist/scripts/preflight-credentials.d.ts.map +1 -0
  82. package/dist/scripts/preflight-credentials.js +108 -0
  83. package/dist/scripts/preflight-credentials.test.d.ts +2 -0
  84. package/dist/scripts/preflight-credentials.test.d.ts.map +1 -0
  85. package/dist/scripts/preflight-credentials.test.js +77 -0
  86. package/dist/scripts/sandbox-args.test.d.ts +2 -0
  87. package/dist/scripts/sandbox-args.test.d.ts.map +1 -0
  88. package/dist/scripts/sandbox-args.test.js +77 -0
  89. package/dist/scripts/sandbox.d.ts +52 -0
  90. package/dist/scripts/sandbox.d.ts.map +1 -1
  91. package/dist/scripts/sandbox.js +92 -15
  92. package/dist/scripts/secret.d.ts +15 -0
  93. package/dist/scripts/secret.d.ts.map +1 -0
  94. package/dist/scripts/secret.js +29 -0
  95. package/dist/scripts/secret.test.d.ts +2 -0
  96. package/dist/scripts/secret.test.d.ts.map +1 -0
  97. package/dist/scripts/secret.test.js +28 -0
  98. package/dist/scripts/telemetry.test.js +2 -0
  99. package/dist/scripts/typegen.d.ts +15 -0
  100. package/dist/scripts/typegen.d.ts.map +1 -0
  101. package/dist/scripts/typegen.js +16 -0
  102. package/dist/secret-naming.d.ts +58 -0
  103. package/dist/secret-naming.d.ts.map +1 -0
  104. package/dist/secret-naming.js +67 -0
  105. package/dist/secret-naming.test.d.ts +2 -0
  106. package/dist/secret-naming.test.d.ts.map +1 -0
  107. package/dist/secret-naming.test.js +40 -0
  108. package/dist/telemetry/environment.d.ts.map +1 -1
  109. package/dist/telemetry/environment.js +3 -1
  110. package/dist/telemetry/telemetry.test.js +10 -0
  111. package/dist/version.d.ts +1 -1
  112. package/dist/version.d.ts.map +1 -1
  113. package/dist/version.js +1 -1
  114. package/package.json +7 -1
  115. package/src/cdk/__fixtures__/import-meta-handler.ts +16 -0
  116. package/src/cdk/blocks-backend.test.ts +308 -237
  117. package/src/cdk/blocks-backend.ts +98 -77
  118. package/src/cdk/blocks-defaults.test.ts +27 -0
  119. package/src/cdk/blocks-defaults.ts +75 -0
  120. package/src/cdk/blocks-stack.test.ts +160 -110
  121. package/src/cdk/bundling.test.ts +90 -0
  122. package/src/cdk/bundling.ts +76 -0
  123. package/src/cdk/compute/compute.ts +37 -0
  124. package/src/cdk/compute/default-compute-factory.ts +37 -0
  125. package/src/cdk/index.ts +178 -27
  126. package/src/cdk/internal.ts +30 -0
  127. package/src/cdk/mixins.test.ts +56 -1
  128. package/src/cdk/mixins.ts +32 -7
  129. package/src/common/index.ts +9 -0
  130. package/src/hosting-secrets.test.ts +106 -0
  131. package/src/hosting-secrets.ts +106 -0
  132. package/src/hosting.test.ts +244 -63
  133. package/src/hosting.ts +180 -68
  134. package/src/index.cdk.ts +79 -36
  135. package/src/index.ts +35 -16
  136. package/src/lambda-handler.test.ts +74 -0
  137. package/src/pipeline/index.ts +64 -11
  138. package/src/pipeline.test.ts +55 -0
  139. package/src/rpc.test.ts +66 -1
  140. package/src/rpc.ts +60 -3
  141. package/src/scripts/config.ts +35 -0
  142. package/src/scripts/deploy.ts +5 -0
  143. package/src/scripts/dev-server.ts +40 -0
  144. package/src/scripts/generate-client.test.ts +43 -8
  145. package/src/scripts/generate-client.ts +70 -65
  146. package/src/scripts/index.ts +20 -17
  147. package/src/scripts/preflight-credentials.test.ts +110 -0
  148. package/src/scripts/preflight-credentials.ts +131 -0
  149. package/src/scripts/sandbox-args.test.ts +92 -0
  150. package/src/scripts/sandbox.ts +102 -15
  151. package/src/scripts/secret.test.ts +35 -0
  152. package/src/scripts/secret.ts +35 -0
  153. package/src/scripts/telemetry.test.ts +2 -0
  154. package/src/scripts/typegen.ts +18 -0
  155. package/src/secret-naming.test.ts +59 -0
  156. package/src/secret-naming.ts +99 -0
  157. package/src/telemetry/environment.ts +3 -1
  158. package/src/telemetry/telemetry.test.ts +12 -0
  159. package/src/version.ts +1 -1
package/dist/rpc.js CHANGED
@@ -11,6 +11,27 @@
11
11
  import { ApiError } from './errors.js';
12
12
  // ── Constants ───────────────────────────────────────────────────────────────
13
13
  const VERSION = '2.0';
14
+ /**
15
+ * Maximum accepted request-body size, in bytes (10 MiB).
16
+ *
17
+ * Matches the payload limit API Gateway enforces in production (~10 MB for a
18
+ * REST API). In prod an oversized body is rejected by API Gateway at the edge —
19
+ * before the Lambda is invoked — so this guard's rejection path effectively
20
+ * runs on the dev/mock server, where there is no edge to stop it: an oversized
21
+ * body would otherwise buffer and wedge the local database (e.g. PGlite). By
22
+ * enforcing the *same* limit locally, the dev server rejects the same oversized
23
+ * body prod would 413 at the edge, instead of failing only in one environment.
24
+ * Enforced in `parseRpcRequest`, the single choke point both the Lambda handler
25
+ * and the dev server route through.
26
+ *
27
+ * Note: this value is the Lambda + API Gateway limit. It lives here (in the
28
+ * compute-agnostic parser) because Lambda is the only compute path today. When
29
+ * a non-API-Gateway compute (e.g. container/ALB) is introduced, this should
30
+ * become compute-aware — sourced from / overridden by the compute layer (via
31
+ * the Compute `setEnv` config hook) rather than a fixed constant in `core` —
32
+ * since an ALB has a different payload limit. Tracked with the multi-compute work.
33
+ */
34
+ export const MAX_RPC_BODY_BYTES = 10 * 1024 * 1024;
14
35
  /** Reserved JSON-RPC error codes. */
15
36
  export const RpcErrorCode = {
16
37
  ParseError: -32700,
@@ -58,6 +79,19 @@ export function decodeRpcResponse(body) {
58
79
  * anything about the JSON-RPC spec.
59
80
  */
60
81
  export function parseRpcRequest(bodyText) {
82
+ // Reject oversized bodies before parsing or dispatch. See MAX_RPC_BODY_BYTES
83
+ // for the limit and its rationale.
84
+ if (Buffer.byteLength(bodyText, 'utf8') > MAX_RPC_BODY_BYTES) {
85
+ // Emit the real HTTP status (413) as the error code, not a reserved -32xxx:
86
+ // `decodeRpcResponse` maps a positive code straight to `ApiError.status`
87
+ // (reserved codes collapse to 500), mirroring the 504 handler-timeout path,
88
+ // so a caller's `e.status === 413` works. `name` (which crosses the wire,
89
+ // not the code) lets callers match with `isBlocksError(e, 'PayloadTooLarge')`.
90
+ return {
91
+ ok: false,
92
+ response: errorResponse(413, `Request body exceeds the ${MAX_RPC_BODY_BYTES} byte (${MAX_RPC_BODY_BYTES / (1024 * 1024)} MiB) limit`, null, { name: 'PayloadTooLarge' }),
93
+ };
94
+ }
61
95
  let parsed;
62
96
  try {
63
97
  parsed = JSON.parse(bodyText || '{}');
@@ -79,15 +113,24 @@ export function parseRpcRequest(bodyText) {
79
113
  response: errorResponse(RpcErrorCode.InvalidRequest, 'Invalid Request: method must be "namespace.method"', id, { name: 'InvalidRequest' }),
80
114
  };
81
115
  }
116
+ const hasParams = Object.hasOwn(parsed, 'params');
117
+ if (hasParams && (parsed.params === null || typeof parsed.params !== 'object')) {
118
+ return {
119
+ ok: false,
120
+ response: errorResponse(RpcErrorCode.InvalidParams, 'Invalid params: expected an array or object', id, { name: 'InvalidParams' }),
121
+ };
122
+ }
123
+ let args = [];
124
+ if (hasParams) {
125
+ args = Array.isArray(parsed.params) ? parsed.params : Object.values(parsed.params);
126
+ }
82
127
  return {
83
128
  ok: true,
84
129
  request: {
85
130
  apiNamespace: parsed.method.substring(0, dotIndex),
86
131
  method: parsed.method.substring(dotIndex + 1),
87
132
  // JSON-RPC 2.0 §4.2: params may be an array (positional) or object (named).
88
- args: Array.isArray(parsed.params)
89
- ? parsed.params
90
- : Object.values(parsed.params ?? {}),
133
+ args,
91
134
  id,
92
135
  },
93
136
  };
package/dist/rpc.test.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { describe, it } from 'node:test';
4
4
  import assert from 'node:assert';
5
- import { decodeRpcResponse, errorResponseFromCatch, parseRpcRequest, RpcErrorCode } from './rpc.js';
5
+ import { decodeRpcResponse, errorResponseFromCatch, parseRpcRequest, RpcErrorCode, MAX_RPC_BODY_BYTES } from './rpc.js';
6
6
  import { ApiError, isBlocksError } from './errors.js';
7
7
  describe('-32600 Invalid Request error shape', () => {
8
8
  it('returns proper JSON-RPC 2.0 envelope with error code', () => {
@@ -82,6 +82,26 @@ describe('params decoding', () => {
82
82
  assert.deepStrictEqual(result.request.args, []);
83
83
  });
84
84
  });
85
+ describe('-32602 Invalid Params validation', () => {
86
+ for (const params of ['abc', 42, true, false, null]) {
87
+ it(`rejects ${JSON.stringify(params)} params`, () => {
88
+ const result = parseRpcRequest(JSON.stringify({
89
+ jsonrpc: '2.0',
90
+ method: 'api.echo',
91
+ params,
92
+ id: 'request-1',
93
+ }));
94
+ assert.strictEqual(result.ok, false);
95
+ if (!result.ok) {
96
+ const response = JSON.parse(result.response);
97
+ assert.strictEqual(response.error.code, RpcErrorCode.InvalidParams);
98
+ assert.strictEqual(response.error.data.name, 'InvalidParams');
99
+ assert.ok(response.error.message.includes('expected an array or object'));
100
+ assert.strictEqual(response.id, 'request-1');
101
+ }
102
+ });
103
+ }
104
+ });
85
105
  describe('batch requests (top-level JSON array body)', () => {
86
106
  it('rejects an array body as Invalid Request with a null id', () => {
87
107
  const result = parseRpcRequest(JSON.stringify([
@@ -134,3 +154,40 @@ describe('ApiError status ↔ JSON-RPC error code', () => {
134
154
  assert.throws(() => decodeRpcResponse({ jsonrpc: '2.0', error: { code: RpcErrorCode.InvalidRequest, message: 'Invalid Request' }, id: null }), (e) => e instanceof ApiError && e.status === 500);
135
155
  });
136
156
  });
157
+ describe('request body size limit', () => {
158
+ it('accepts a normal-sized body', () => {
159
+ const result = parseRpcRequest(JSON.stringify({ jsonrpc: '2.0', method: 'ns.method', params: [], id: 1 }));
160
+ assert.strictEqual(result.ok, true);
161
+ });
162
+ it('rejects a body larger than MAX_RPC_BODY_BYTES with a PayloadTooLarge error', () => {
163
+ // A JSON string just over the limit (a single huge param value).
164
+ const huge = 'x'.repeat(MAX_RPC_BODY_BYTES + 1);
165
+ const body = JSON.stringify({ jsonrpc: '2.0', method: 'ns.method', params: [huge], id: 1 });
166
+ assert.ok(Buffer.byteLength(body, 'utf8') > MAX_RPC_BODY_BYTES);
167
+ const result = parseRpcRequest(body);
168
+ assert.strictEqual(result.ok, false);
169
+ if (result.ok)
170
+ return; // narrow
171
+ const parsed = JSON.parse(result.response);
172
+ // Positive HTTP status (413), not a reserved -32xxx — so decodeRpcResponse
173
+ // surfaces it to the client as ApiError.status === 413.
174
+ assert.strictEqual(parsed.error.code, 413);
175
+ assert.strictEqual(parsed.error.data?.name, 'PayloadTooLarge');
176
+ assert.match(parsed.error.message, /exceeds/);
177
+ assert.match(parsed.error.message, /10 MiB/); // human-readable size crosses the wire
178
+ // Client round-trip: decodeRpcResponse surfaces it as ApiError.status 413
179
+ // (not 500), so consumer `e.status === 413` handling works.
180
+ assert.throws(() => decodeRpcResponse(parsed), (e) => e instanceof ApiError && e.status === 413 && isBlocksError(e, 'PayloadTooLarge'));
181
+ });
182
+ it('rejects an oversized body before attempting to parse it (invalid JSON still 413s, not a ParseError)', () => {
183
+ // Oversized AND not valid JSON — the size guard must win, proving the body
184
+ // is rejected before the (expensive) parse + before any handler/DB touch.
185
+ const oversizedGarbage = 'x'.repeat(MAX_RPC_BODY_BYTES + 1);
186
+ const result = parseRpcRequest(oversizedGarbage);
187
+ assert.strictEqual(result.ok, false);
188
+ if (result.ok)
189
+ return;
190
+ const parsed = JSON.parse(result.response);
191
+ assert.strictEqual(parsed.error.data?.name, 'PayloadTooLarge');
192
+ });
193
+ });
@@ -0,0 +1,15 @@
1
+ /** Set (create or overwrite) a Blocks config value. */
2
+ export declare function setConfig(key: string, value: string, opts?: {
3
+ stage?: string;
4
+ }): Promise<void>;
5
+ /** List Blocks config keys (names only). */
6
+ export declare function listConfig(opts?: {
7
+ stage?: string;
8
+ }): Promise<string[]>;
9
+ /** Remove a Blocks config value. */
10
+ export declare function removeConfig(key: string, opts?: {
11
+ stage?: string;
12
+ }): Promise<boolean>;
13
+ /** CLI dispatcher for `blocks config <set|list|remove> …`. */
14
+ export declare function runConfigCli(argv: string[]): Promise<void>;
15
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/scripts/config.ts"],"names":[],"mappings":"AAgBA,uDAAuD;AACvD,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,UAAU,CAAC,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAE3E;AAED,oCAAoC;AACpC,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 config` — manage a Blocks app's non-sensitive **config** values (SSM
5
+ * Parameter Store), pinned to the Blocks `/blocks/<stackId>/config` namespace
6
+ * (scoped to the app's stable `stackId` from `.blocks/config.json`). Thin wrapper
7
+ * over the shared `@aws-blocks/hosting` CLI with the kind fixed to `config`. The
8
+ * `secret` counterpart is `./secret.ts`.
9
+ *
10
+ * @module
11
+ */
12
+ import { listValues, removeValue, runValueCli, setValue } from '@aws-blocks/hosting/scripts';
13
+ import { blocksConfigPrefix } from '../secret-naming.js';
14
+ /** Set (create or overwrite) a Blocks config value. */
15
+ export function setConfig(key, value, opts = {}) {
16
+ return setValue('config', key, value, { prefix: blocksConfigPrefix(), ...opts });
17
+ }
18
+ /** List Blocks config keys (names only). */
19
+ export function listConfig(opts = {}) {
20
+ return listValues('config', { prefix: blocksConfigPrefix(), ...opts });
21
+ }
22
+ /** Remove a Blocks config value. */
23
+ export function removeConfig(key, opts = {}) {
24
+ return removeValue('config', key, { prefix: blocksConfigPrefix(), ...opts });
25
+ }
26
+ /** CLI dispatcher for `blocks config <set|list|remove> …`. */
27
+ export function runConfigCli(argv) {
28
+ return runValueCli(argv, { kind: 'config', prefix: blocksConfigPrefix(), label: 'blocks config' });
29
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/scripts/deploy.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,iBAiGlD"}
1
+ {"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/scripts/deploy.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,iBAqGlD"}
@@ -5,6 +5,7 @@ import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
5
5
  import { join, resolve, dirname } from 'node:path';
6
6
  import { fileURLToPath, pathToFileURL } from 'node:url';
7
7
  import { ensureSecrets, loadProductionEnv } 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 { getCdkTelemetryEnv } from './cdk-telemetry-env.js';
@@ -15,6 +16,9 @@ export async function deploy(options) {
15
16
  // Load production environment (from .env.production or CI env vars)
16
17
  loadProductionEnv();
17
18
  process.env.BLOCKS_STAGE = 'production';
19
+ // Fail fast if AWS credentials are missing/expired, before generating the
20
+ // client and spending time in synth only to hit an opaque CDK credential error.
21
+ await assertAwsCredentials('deploy');
18
22
  // Provision secrets for production. projectRoot must match the root cdk
19
23
  // synth uses (passed as --context below) so the written parameter name
20
24
  // equals the one the app resolves at synth.
@@ -43,6 +43,14 @@ export interface DevServerOptions {
43
43
  frontendCommand?: string;
44
44
  /** Port the frontend dev server listens on. Default: 3100. */
45
45
  frontendPort?: number;
46
+ /**
47
+ * Watch `secret()` / `config()` calls and regenerate the type-safe key
48
+ * augmentation (so `getSecret`/`getConfig` autocomplete and reject typos) as you
49
+ * edit — all under this one `npm run dev`, no second command. Auto-detected: a
50
+ * no-op unless the app actually declares a `secret()`/`config()`, and never fatal
51
+ * (a failure only logs a warning). Set `false` to disable. Default: enabled.
52
+ */
53
+ typegen?: boolean;
46
54
  }
47
55
  /**
48
56
  * Single-shot TCP probe: resolves `true` iff a connection to `port` succeeds
@@ -1 +1 @@
1
- {"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../src/scripts/dev-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../../src/scripts/dev-server.ts"],"names":[],"mappings":"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"}
@@ -75,6 +75,31 @@ export function buildBlocksConfig(port, isSandbox) {
75
75
  export function isBlocksConfigRequest(method, pathname) {
76
76
  return method === 'GET' && pathname === `${BLOCKS_SANDBOX_PREFIX}/config.json`;
77
77
  }
78
+ /**
79
+ * Bootstrap the type-safe `getSecret`/`getConfig` key generation for the dev
80
+ * session. Auto-detected and non-fatal: scans the app for `secret()`/`config()`
81
+ * calls and, only if it finds any, generates the augmentation `.d.ts` and starts a
82
+ * watcher that regenerates on save — so a single `npm run dev` gives type-safe keys
83
+ * with no second command. Returns a `stop()` (or `undefined` when the app declares
84
+ * no secrets, typegen is unavailable, or it is disabled). The watcher is `unref`'d
85
+ * so it can never block the dev server's shutdown.
86
+ */
87
+ async function startTypegenWatch() {
88
+ try {
89
+ const { scanValueKeys, watchHostingValues } = await import('@aws-blocks/hosting/scripts');
90
+ const scan = await scanValueKeys();
91
+ if (scan.secretKeys.length === 0 && scan.configKeys.length === 0) {
92
+ return undefined; // app doesn't use secret()/config() → nothing to type, skip silently
93
+ }
94
+ console.log('🔑 Type-safe secret()/config() keys — watching for changes...');
95
+ return await watchHostingValues({ unref: true });
96
+ }
97
+ catch (error) {
98
+ // Never break the dev server over typegen (e.g. `typescript` not installed).
99
+ console.warn(`⚠️ hosting-typegen skipped: ${error instanceof Error ? error.message : String(error)}`);
100
+ return undefined;
101
+ }
102
+ }
78
103
  /**
79
104
  * Initialize all building blocks that have an initialize() method.
80
105
  * This is the "local deploy" phase - mirrors CDK deploy.
@@ -790,6 +815,11 @@ export async function startDevServer(options) {
790
815
  console.log('📝 Generating client code...');
791
816
  await writeClientCode(resolvedPath, clientPath);
792
817
  }
818
+ // Type-safe getSecret/getConfig: generate + watch the key augmentation so the
819
+ // getters autocomplete and reject typos, regenerating on save — all under this
820
+ // one `npm run dev`. Auto-detected (no-op unless the app uses secret()/config())
821
+ // and non-fatal.
822
+ const stopTypegen = options.typegen === false ? undefined : await startTypegenWatch();
793
823
  // ── Startup reclaim ──────────────────────────────────────────────────────
794
824
  // Free any port left bound by a crashed / SIGKILL'd predecessor before we bind
795
825
  // the front door or spawn the `--strictPort` frontend. tsx-watch only gives the
@@ -875,6 +905,7 @@ export async function startDevServer(options) {
875
905
  clearTimeout(respawnTimer);
876
906
  respawnTimer = null;
877
907
  }
908
+ stopTypegen?.(); // tear down the typegen watcher (unref'd, but close it cleanly)
878
909
  // Detach our own listeners so repeated signals can't pile up handlers.
879
910
  for (const sig of signals)
880
911
  process.removeListener(sig, cleanup);
@@ -1 +1 @@
1
- {"version":3,"file":"generate-client.d.ts","sourceRoot":"","sources":["../../src/scripts/generate-client.ts"],"names":[],"mappings":"AAQA;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4DhF;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/F"}
1
+ {"version":3,"file":"generate-client.d.ts","sourceRoot":"","sources":["../../src/scripts/generate-client.ts"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgEhF;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/F"}
@@ -1,8 +1,9 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { writeFileSync, mkdirSync } from 'fs';
4
- import { dirname } from 'path';
5
- import { pathToFileURL } from 'url';
3
+ import { isManagedValue } from '@aws-blocks/hosting';
4
+ import { mkdirSync, writeFileSync } from 'node:fs';
5
+ import { dirname } from 'node:path';
6
+ import { pathToFileURL } from 'node:url';
6
7
  import { API_NAMESPACE_MARKER } from '../api.js';
7
8
  /**
8
9
  * Generate browser-safe client code from backend exports.
@@ -43,6 +44,10 @@ export async function generateClientCode(foundationPath) {
43
44
  const isScope = typeof value?.id === 'string' && typeof value?.fullId === 'string';
44
45
  if (isScope)
45
46
  continue;
47
+ // A secret()/config() marker is a deferred value, not an API — skip it, or
48
+ // `export const k = secret('K')` in index.ts would become a bogus HTTP namespace.
49
+ if (isManagedValue(value))
50
+ continue;
46
51
  // Fallback: any exported function/object gets an ApiNamespace proxy.
47
52
  if ((typeof value === 'function' || typeof value === 'object') && value !== null) {
48
53
  lines.push(`export const ${name} = __BLOCKS_ApiNamespaceClient__('${name}');`);
@@ -50,7 +55,7 @@ export async function generateClientCode(foundationPath) {
50
55
  }
51
56
  }
52
57
  // Emit generateClient — creates new ApiNamespaceClient instances with the given url
53
- const nsEntries = namespaceNames.map(n => ` ${n}: __BLOCKS_ApiNamespaceClient__('${n}', config),`).join('\n');
58
+ const nsEntries = namespaceNames.map((n) => ` ${n}: __BLOCKS_ApiNamespaceClient__('${n}', config),`).join('\n');
54
59
  lines.push('');
55
60
  lines.push(`export const generateClient = (config) => ({\n${nsEntries}\n});`);
56
61
  const header = [
@@ -1,11 +1,11 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { describe, it, beforeEach, afterEach } from 'node:test';
4
3
  import assert from 'node:assert';
4
+ import { afterEach, beforeEach, describe, it } from 'node:test';
5
+ import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
6
+ import { tmpdir } from 'node:os';
7
+ import { join } from 'node:path';
5
8
  import { generateClientCode } from './generate-client.js';
6
- import { writeFileSync, mkdirSync, rmSync } from 'fs';
7
- import { join } from 'path';
8
- import { tmpdir } from 'os';
9
9
  /**
10
10
  * Regression test: generateClientCode collects middleware from whatever
11
11
  * the backend registers via registerClientMiddleware(). In sandbox mode,
@@ -45,4 +45,19 @@ describe('generateClientCode — middleware collection', () => {
45
45
  assert.ok(code.includes("import '@aws-blocks/bb-realtime/mock-middleware'"), 'Generated client must include mock-middleware in local dev mode');
46
46
  assert.ok(!code.includes('aws-middleware'), 'Generated client must NOT include aws-middleware in local dev mode');
47
47
  });
48
+ it('skips secret()/config() markers — they are values, not API namespaces', async () => {
49
+ // A secret()/config() marker is branded with Symbol.for('@aws-blocks/hosting.ManagedValue');
50
+ // build one inline so the fixture needs no imports. A real export still gets a proxy.
51
+ const backendPath = join(tmpDir, 'backend.mjs');
52
+ writeFileSync(backendPath, `
53
+ const BRAND = Symbol.for('@aws-blocks/hosting.ManagedValue');
54
+ export const stripeKey = { [BRAND]: true, key: 'STRIPE_KEY', kind: 'secret' };
55
+ export const featureFlags = { [BRAND]: true, key: 'FEATURE_FLAGS', kind: 'config' };
56
+ export function realApi() {}
57
+ `);
58
+ const code = await generateClientCode(backendPath);
59
+ assert.ok(!code.includes("__BLOCKS_ApiNamespaceClient__('stripeKey')"), 'a secret() marker must not become an HTTP namespace');
60
+ assert.ok(!code.includes("__BLOCKS_ApiNamespaceClient__('featureFlags')"), 'a config() marker must not become an HTTP namespace');
61
+ assert.ok(code.includes("export const realApi = __BLOCKS_ApiNamespaceClient__('realApi')"), 'a real (non-marker) export still becomes a namespace');
62
+ });
48
63
  });
@@ -1,13 +1,16 @@
1
- export { startDevServer, type DevServerOptions } from './dev-server.js';
2
- export { startSandbox, destroySandbox, type SandboxOptions } from './sandbox.js';
1
+ export { type BuildAndSendEventOptions, buildAndSendEvent, type CommandName, type CommandState, classifyError, trackCommand, } from '../telemetry/index.js';
2
+ export { listConfig, removeConfig, runConfigCli, setConfig } from './config.js';
3
+ export { type ConsoleOptions, openConsole } from './console.js';
4
+ export { type DeployOptions, deploy } from './deploy.js';
5
+ export { type DestroyOptions, destroy } from './destroy.js';
6
+ export { type DevServerOptions, startDevServer } from './dev-server.js';
7
+ export { ensureSecrets, loadEnvFile, loadProductionEnv } from './ensure-secrets.js';
3
8
  export { generateClientCode, writeClientCode } from './generate-client.js';
4
9
  export { generateSpec, writeSpec } from './generate-spec.js';
5
- export { validateSpec, type SpecValidationError } from './validate-spec.js';
6
- export { deploy, type DeployOptions } from './deploy.js';
7
- export { destroy, type DestroyOptions } from './destroy.js';
8
- export { openConsole, type ConsoleOptions } from './console.js';
9
- export { ensureSecrets, loadProductionEnv, loadEnvFile } from './ensure-secrets.js';
10
- export { trackCommand, buildAndSendEvent, classifyError, type CommandName, type CommandState, type BuildAndSendEventOptions, } from '../telemetry/index.js';
11
- export { telemetry, type TelemetryOptions } from './telemetry.js';
12
- export { getStackId, getSandboxId, getStackName } from './stack-id.js';
10
+ export { destroySandbox, type SandboxOptions, startSandbox } from './sandbox.js';
11
+ export { listSecrets, removeSecret, runSecretCli, setSecret } from './secret.js';
12
+ export { getSandboxId, getStackId, getStackName } from './stack-id.js';
13
+ export { type TelemetryOptions, telemetry } from './telemetry.js';
14
+ export { runTypegenCli } from './typegen.js';
15
+ export { type SpecValidationError, validateSpec } from './validate-spec.js';
13
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scripts/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,MAAM,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,wBAAwB,GAC9B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scripts/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,KAAK,wBAAwB,EAC7B,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,aAAa,EACb,YAAY,GACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,KAAK,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,KAAK,cAAc,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,KAAK,gBAAgB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,KAAK,mBAAmB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
@@ -1,14 +1,17 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
+ export { buildAndSendEvent, classifyError, trackCommand, } from '../telemetry/index.js';
4
+ export { listConfig, removeConfig, runConfigCli, setConfig } from './config.js';
5
+ export { openConsole } from './console.js';
6
+ export { deploy } from './deploy.js';
7
+ export { destroy } from './destroy.js';
3
8
  export { startDevServer } from './dev-server.js';
4
- export { startSandbox, destroySandbox } from './sandbox.js';
9
+ export { ensureSecrets, loadEnvFile, loadProductionEnv } from './ensure-secrets.js';
5
10
  export { generateClientCode, writeClientCode } from './generate-client.js';
6
11
  export { generateSpec, writeSpec } from './generate-spec.js';
7
- export { validateSpec } from './validate-spec.js';
8
- export { deploy } from './deploy.js';
9
- export { destroy } from './destroy.js';
10
- export { openConsole } from './console.js';
11
- export { ensureSecrets, loadProductionEnv, loadEnvFile } from './ensure-secrets.js';
12
- export { trackCommand, buildAndSendEvent, classifyError, } from '../telemetry/index.js';
12
+ export { destroySandbox, startSandbox } from './sandbox.js';
13
+ export { listSecrets, removeSecret, runSecretCli, setSecret } from './secret.js';
14
+ export { getSandboxId, getStackId, getStackName } from './stack-id.js';
13
15
  export { telemetry } from './telemetry.js';
14
- export { getStackId, getSandboxId, getStackName } from './stack-id.js';
16
+ export { runTypegenCli } from './typegen.js';
17
+ export { validateSpec } from './validate-spec.js';
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Resolve the region for the STS probe from the environment. Returns `null`
3
+ * when neither `AWS_REGION` nor `AWS_DEFAULT_REGION` is set — the caller then
4
+ * skips the probe rather than guessing a region, because a hardcoded guess
5
+ * (e.g. `us-east-1`) belongs to a different **partition** than GovCloud
6
+ * (`us-gov-*`) or China (`cn-*`) and would fail against a partition the user
7
+ * isn't even deploying to.
8
+ */
9
+ export declare function resolveProbeRegion(env?: NodeJS.ProcessEnv): string | null;
10
+ /**
11
+ * Probe that resolves when valid AWS credentials are available for `region` and
12
+ * rejects otherwise. Injectable so the guard can be unit-tested without network
13
+ * or real credentials; production callers use the default {@link stsProbe}.
14
+ */
15
+ export type CredentialProbe = (region: string) => Promise<void>;
16
+ /**
17
+ * Verify AWS credentials before a deploy command spends time synthesizing, and
18
+ * fail fast with actionable guidance when they're missing, expired, or invalid.
19
+ *
20
+ * Skips silently (with a warning) when no region can be resolved from the
21
+ * environment, and treats network/service errors as non-fatal — see the module
22
+ * doc for the rationale.
23
+ *
24
+ * @param command - The npm script name, used in the messages (e.g. `sandbox`, `deploy`).
25
+ * @param probe - Credential probe; defaults to STS GetCallerIdentity. Override in tests.
26
+ * @param env - Environment to resolve the region from. Defaults to `process.env`.
27
+ * @throws {Error} With actionable guidance when the probe reports a credential error.
28
+ */
29
+ export declare function assertAwsCredentials(command: string, probe?: CredentialProbe, env?: NodeJS.ProcessEnv): Promise<void>;
30
+ //# sourceMappingURL=preflight-credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight-credentials.d.ts","sourceRoot":"","sources":["../../src/scripts/preflight-credentials.ts"],"names":[],"mappings":"AAmCA;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,IAAI,CAEtF;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAyBhE;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,eAA0B,EACjC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAClC,OAAO,CAAC,IAAI,CAAC,CAoCf"}
@@ -0,0 +1,108 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Pre-deploy AWS credential check.
5
+ *
6
+ * `cdk deploy` spends ~10 seconds synthesizing the app before it makes its
7
+ * first AWS call, so a missing or expired credential surfaces only *after* that
8
+ * wasted synth — as an opaque CDK/CloudFormation error that doesn't name the
9
+ * real cause. Calling STS GetCallerIdentity up front turns that into an
10
+ * immediate, actionable message the moment a deploy command starts.
11
+ *
12
+ * The check is intentionally conservative: it fails fast **only** on a real
13
+ * credential problem (missing/expired/invalid). A network or service error
14
+ * (STS unreachable, throttled, disabled in a region) is not treated as a
15
+ * credential failure — it warns and lets the deploy proceed, since blocking on
16
+ * an unverifiable probe would reject a deploy that might have worked.
17
+ */
18
+ /**
19
+ * Error `name`s that mean the credentials themselves are missing, expired, or
20
+ * invalid — the cases where the "configure your credentials" remediation is
21
+ * correct. Anything else (network, throttling, an explicit `AccessDenied` — which
22
+ * actually proves the identity resolved) is surfaced as itself instead.
23
+ */
24
+ const CREDENTIAL_ERROR_NAMES = new Set([
25
+ 'CredentialsProviderError',
26
+ 'ExpiredToken',
27
+ 'ExpiredTokenException',
28
+ 'InvalidClientTokenId',
29
+ 'UnrecognizedClientException',
30
+ 'SignatureDoesNotMatch',
31
+ 'TokenRefreshRequired',
32
+ ]);
33
+ /**
34
+ * Resolve the region for the STS probe from the environment. Returns `null`
35
+ * when neither `AWS_REGION` nor `AWS_DEFAULT_REGION` is set — the caller then
36
+ * skips the probe rather than guessing a region, because a hardcoded guess
37
+ * (e.g. `us-east-1`) belongs to a different **partition** than GovCloud
38
+ * (`us-gov-*`) or China (`cn-*`) and would fail against a partition the user
39
+ * isn't even deploying to.
40
+ */
41
+ export function resolveProbeRegion(env = process.env) {
42
+ return env.AWS_REGION || env.AWS_DEFAULT_REGION || null;
43
+ }
44
+ /**
45
+ * Default probe: STS GetCallerIdentity via the SDK's standard credential chain
46
+ * (env vars, shared config/credentials, SSO, container/instance roles).
47
+ *
48
+ * Dynamically imported so the STS client is only loaded when a deploy actually
49
+ * runs — never during dev-server startup or a mock unit test. Bounded with a
50
+ * short request timeout and a single attempt (matching `common/config.ts`) so a
51
+ * bad network can't make this hang *longer* than the synth it's replacing.
52
+ */
53
+ const stsProbe = async (region) => {
54
+ const { STSClient, GetCallerIdentityCommand } = await import('@aws-sdk/client-sts');
55
+ const client = new STSClient({
56
+ region,
57
+ maxAttempts: 1,
58
+ requestHandler: { connectionTimeout: 2000, requestTimeout: 3000 },
59
+ });
60
+ try {
61
+ await client.send(new GetCallerIdentityCommand({}));
62
+ }
63
+ finally {
64
+ client.destroy();
65
+ }
66
+ };
67
+ /**
68
+ * Verify AWS credentials before a deploy command spends time synthesizing, and
69
+ * fail fast with actionable guidance when they're missing, expired, or invalid.
70
+ *
71
+ * Skips silently (with a warning) when no region can be resolved from the
72
+ * environment, and treats network/service errors as non-fatal — see the module
73
+ * doc for the rationale.
74
+ *
75
+ * @param command - The npm script name, used in the messages (e.g. `sandbox`, `deploy`).
76
+ * @param probe - Credential probe; defaults to STS GetCallerIdentity. Override in tests.
77
+ * @param env - Environment to resolve the region from. Defaults to `process.env`.
78
+ * @throws {Error} With actionable guidance when the probe reports a credential error.
79
+ */
80
+ export async function assertAwsCredentials(command, probe = stsProbe, env = process.env) {
81
+ const region = resolveProbeRegion(env);
82
+ if (!region) {
83
+ console.warn(`⚠️ Skipping the AWS credential pre-check for \`npm run ${command}\`: ` +
84
+ 'no region set in AWS_REGION / AWS_DEFAULT_REGION. The deploy will surface any credential error itself.');
85
+ return;
86
+ }
87
+ try {
88
+ await probe(region);
89
+ }
90
+ catch (error) {
91
+ // Use the error *name* only — never the raw message, which for an STS
92
+ // authorization failure embeds the caller ARN and account id (and this
93
+ // Error propagates through telemetry).
94
+ const name = error instanceof Error && error.name ? error.name : 'UnknownError';
95
+ if (CREDENTIAL_ERROR_NAMES.has(name)) {
96
+ throw new Error(`AWS credentials could not be verified for \`npm run ${command}\` (${name}).\n` +
97
+ 'Configure AWS credentials — for example:\n' +
98
+ ' • run `aws configure` (or `aws sso login`), or\n' +
99
+ ' • set AWS_PROFILE to a configured profile, or\n' +
100
+ ' • export AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY (and AWS_SESSION_TOKEN for temporary credentials).\n' +
101
+ `Then re-run \`npm run ${command}\`.`);
102
+ }
103
+ // Not a credential problem (network, throttling, STS disabled in-region, …).
104
+ // Don't block a deploy that might still work — warn and continue.
105
+ console.warn(`⚠️ Could not verify AWS credentials for \`npm run ${command}\` (${name}); ` +
106
+ 'continuing — the deploy will report any real error.');
107
+ }
108
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=preflight-credentials.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight-credentials.test.d.ts","sourceRoot":"","sources":["../../src/scripts/preflight-credentials.test.ts"],"names":[],"mappings":""}