@aws-blocks/core 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +13 -0
  2. package/dist/api.d.ts.map +1 -1
  3. package/dist/api.js +32 -0
  4. package/dist/api.test.js +22 -0
  5. package/dist/bb-utils.d.ts +1 -0
  6. package/dist/bb-utils.d.ts.map +1 -1
  7. package/dist/bb-utils.js +3 -0
  8. package/dist/cdk/apigateway-account.d.ts +33 -0
  9. package/dist/cdk/apigateway-account.d.ts.map +1 -0
  10. package/dist/cdk/apigateway-account.js +60 -0
  11. package/dist/cdk/blocks-backend.d.ts +43 -9
  12. package/dist/cdk/blocks-backend.d.ts.map +1 -1
  13. package/dist/cdk/blocks-backend.js +69 -73
  14. package/dist/cdk/blocks-backend.test.js +78 -69
  15. package/dist/cdk/blocks-defaults.d.ts +92 -2
  16. package/dist/cdk/blocks-defaults.d.ts.map +1 -1
  17. package/dist/cdk/blocks-defaults.js +22 -2
  18. package/dist/cdk/blocks-defaults.test.js +21 -2
  19. package/dist/cdk/blocks-stack.test.js +94 -37
  20. package/dist/cdk/compute/compute-registry.d.ts +19 -0
  21. package/dist/cdk/compute/compute-registry.d.ts.map +1 -0
  22. package/dist/cdk/compute/compute-registry.js +38 -0
  23. package/dist/cdk/compute/compute.d.ts +2 -0
  24. package/dist/cdk/compute/compute.d.ts.map +1 -1
  25. package/dist/cdk/compute/compute.js +8 -0
  26. package/dist/cdk/compute/default-compute-factory.d.ts +34 -0
  27. package/dist/cdk/compute/default-compute-factory.d.ts.map +1 -0
  28. package/dist/cdk/compute/default-compute-factory.js +3 -0
  29. package/dist/cdk/config-registry.d.ts +34 -4
  30. package/dist/cdk/config-registry.d.ts.map +1 -1
  31. package/dist/cdk/config-registry.js +83 -25
  32. package/dist/cdk/config-registry.test.d.ts +2 -0
  33. package/dist/cdk/config-registry.test.d.ts.map +1 -0
  34. package/dist/cdk/config-registry.test.js +115 -0
  35. package/dist/cdk/index.d.ts +76 -7
  36. package/dist/cdk/index.d.ts.map +1 -1
  37. package/dist/cdk/index.js +99 -9
  38. package/dist/cdk/internal.d.ts +1 -0
  39. package/dist/cdk/internal.d.ts.map +1 -1
  40. package/dist/client/index.d.ts +1 -1
  41. package/dist/client/index.d.ts.map +1 -1
  42. package/dist/client/index.js +1 -1
  43. package/dist/common/config.d.ts +34 -0
  44. package/dist/common/config.d.ts.map +1 -1
  45. package/dist/common/config.js +45 -3
  46. package/dist/common/config.test.js +19 -0
  47. package/dist/common/index.d.ts +8 -0
  48. package/dist/common/index.d.ts.map +1 -1
  49. package/dist/errors.d.ts +16 -0
  50. package/dist/errors.d.ts.map +1 -1
  51. package/dist/errors.js +20 -0
  52. package/dist/hosting-secrets.d.ts +48 -0
  53. package/dist/hosting-secrets.d.ts.map +1 -0
  54. package/dist/hosting-secrets.js +57 -0
  55. package/dist/hosting-secrets.test.d.ts +2 -0
  56. package/dist/hosting-secrets.test.d.ts.map +1 -0
  57. package/dist/hosting-secrets.test.js +81 -0
  58. package/dist/hosting.d.ts +97 -6
  59. package/dist/hosting.d.ts.map +1 -1
  60. package/dist/hosting.js +91 -23
  61. package/dist/hosting.test.js +182 -10
  62. package/dist/index.cdk.d.ts +13 -12
  63. package/dist/index.cdk.d.ts.map +1 -1
  64. package/dist/index.cdk.js +13 -9
  65. package/dist/index.d.ts +8 -7
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +9 -5
  68. package/dist/lambda-handler.d.ts +17 -0
  69. package/dist/lambda-handler.d.ts.map +1 -1
  70. package/dist/lambda-handler.js +52 -3
  71. package/dist/lambda-handler.test.js +196 -1
  72. package/dist/pipeline/index.d.ts +24 -2
  73. package/dist/pipeline/index.d.ts.map +1 -1
  74. package/dist/pipeline/index.js +41 -1
  75. package/dist/pipeline.test.d.ts +2 -0
  76. package/dist/pipeline.test.d.ts.map +1 -0
  77. package/dist/pipeline.test.js +47 -0
  78. package/dist/rpc.d.ts +21 -0
  79. package/dist/rpc.d.ts.map +1 -1
  80. package/dist/rpc.js +34 -0
  81. package/dist/rpc.test.js +38 -1
  82. package/dist/scripts/config.d.ts +15 -0
  83. package/dist/scripts/config.d.ts.map +1 -0
  84. package/dist/scripts/config.js +29 -0
  85. package/dist/scripts/deploy.d.ts.map +1 -1
  86. package/dist/scripts/deploy.js +4 -0
  87. package/dist/scripts/dev-server.d.ts +8 -0
  88. package/dist/scripts/dev-server.d.ts.map +1 -1
  89. package/dist/scripts/dev-server.js +31 -0
  90. package/dist/scripts/generate-client.d.ts.map +1 -1
  91. package/dist/scripts/generate-client.js +9 -4
  92. package/dist/scripts/generate-client.test.js +19 -4
  93. package/dist/scripts/index.d.ts +13 -10
  94. package/dist/scripts/index.d.ts.map +1 -1
  95. package/dist/scripts/index.js +11 -8
  96. package/dist/scripts/preflight-credentials.d.ts +30 -0
  97. package/dist/scripts/preflight-credentials.d.ts.map +1 -0
  98. package/dist/scripts/preflight-credentials.js +108 -0
  99. package/dist/scripts/preflight-credentials.test.d.ts +2 -0
  100. package/dist/scripts/preflight-credentials.test.d.ts.map +1 -0
  101. package/dist/scripts/preflight-credentials.test.js +77 -0
  102. package/dist/scripts/sandbox-args.test.d.ts +2 -0
  103. package/dist/scripts/sandbox-args.test.d.ts.map +1 -0
  104. package/dist/scripts/sandbox-args.test.js +77 -0
  105. package/dist/scripts/sandbox-empty-buckets.test.d.ts +2 -0
  106. package/dist/scripts/sandbox-empty-buckets.test.d.ts.map +1 -0
  107. package/dist/scripts/sandbox-empty-buckets.test.js +171 -0
  108. package/dist/scripts/sandbox.d.ts +106 -0
  109. package/dist/scripts/sandbox.d.ts.map +1 -1
  110. package/dist/scripts/sandbox.js +255 -40
  111. package/dist/scripts/secret.d.ts +15 -0
  112. package/dist/scripts/secret.d.ts.map +1 -0
  113. package/dist/scripts/secret.js +29 -0
  114. package/dist/scripts/secret.test.d.ts +2 -0
  115. package/dist/scripts/secret.test.d.ts.map +1 -0
  116. package/dist/scripts/secret.test.js +28 -0
  117. package/dist/scripts/typegen.d.ts +15 -0
  118. package/dist/scripts/typegen.d.ts.map +1 -0
  119. package/dist/scripts/typegen.js +16 -0
  120. package/dist/secret-naming.d.ts +58 -0
  121. package/dist/secret-naming.d.ts.map +1 -0
  122. package/dist/secret-naming.js +67 -0
  123. package/dist/secret-naming.test.d.ts +2 -0
  124. package/dist/secret-naming.test.d.ts.map +1 -0
  125. package/dist/secret-naming.test.js +40 -0
  126. package/dist/version.d.ts +1 -1
  127. package/dist/version.js +1 -1
  128. package/package.json +4 -1
  129. package/src/api.test.ts +25 -0
  130. package/src/api.ts +39 -0
  131. package/src/bb-utils.ts +3 -0
  132. package/src/cdk/apigateway-account.ts +66 -0
  133. package/src/cdk/blocks-backend.test.ts +291 -272
  134. package/src/cdk/blocks-backend.ts +88 -83
  135. package/src/cdk/blocks-defaults.test.ts +25 -2
  136. package/src/cdk/blocks-defaults.ts +94 -2
  137. package/src/cdk/blocks-stack.test.ts +186 -118
  138. package/src/cdk/compute/compute-registry.ts +45 -0
  139. package/src/cdk/compute/compute.ts +10 -0
  140. package/src/cdk/compute/default-compute-factory.ts +38 -0
  141. package/src/cdk/config-registry.test.ts +135 -0
  142. package/src/cdk/config-registry.ts +92 -34
  143. package/src/cdk/index.ts +129 -10
  144. package/src/cdk/internal.ts +1 -0
  145. package/src/client/index.ts +1 -1
  146. package/src/common/config.test.ts +21 -0
  147. package/src/common/config.ts +47 -3
  148. package/src/common/index.ts +8 -0
  149. package/src/errors.ts +21 -0
  150. package/src/hosting-secrets.test.ts +106 -0
  151. package/src/hosting-secrets.ts +106 -0
  152. package/src/hosting.test.ts +272 -63
  153. package/src/hosting.ts +200 -68
  154. package/src/index.cdk.ts +80 -38
  155. package/src/index.ts +35 -16
  156. package/src/lambda-handler.test.ts +215 -1
  157. package/src/lambda-handler.ts +54 -2
  158. package/src/pipeline/index.ts +64 -11
  159. package/src/pipeline.test.ts +55 -0
  160. package/src/rpc.test.ts +44 -1
  161. package/src/rpc.ts +41 -0
  162. package/src/scripts/config.ts +35 -0
  163. package/src/scripts/deploy.ts +5 -0
  164. package/src/scripts/dev-server.ts +40 -0
  165. package/src/scripts/generate-client.test.ts +43 -8
  166. package/src/scripts/generate-client.ts +70 -65
  167. package/src/scripts/index.ts +20 -17
  168. package/src/scripts/preflight-credentials.test.ts +110 -0
  169. package/src/scripts/preflight-credentials.ts +131 -0
  170. package/src/scripts/sandbox-args.test.ts +92 -0
  171. package/src/scripts/sandbox-empty-buckets.test.ts +191 -0
  172. package/src/scripts/sandbox.ts +287 -39
  173. package/src/scripts/secret.test.ts +35 -0
  174. package/src/scripts/secret.ts +35 -0
  175. package/src/scripts/typegen.ts +18 -0
  176. package/src/secret-naming.test.ts +59 -0
  177. package/src/secret-naming.ts +99 -0
  178. package/src/version.ts +1 -1
@@ -2,9 +2,10 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import { describe, it, beforeEach } from 'node:test';
4
4
  import assert from 'node:assert';
5
- import { createLambdaHandler, _resetCorsPatterns, requestCookies, isApiGatewayHttpEvent, computeHttpDeadlineMs, classifyEvent, buildEventUrl, isLoopbackForwardedHost } from './lambda-handler.js';
5
+ import { createLambdaHandler, _resetCorsPatterns, requestCookies, isApiGatewayHttpEvent, computeHttpDeadlineMs, classifyEvent, buildEventUrl, isLoopbackForwardedHost, TransientConfigError } from './lambda-handler.js';
6
6
  import { registerRoute, clearRouteRegistry } from './raw-route.js';
7
7
  import { decodeRpcResponse } from './rpc.js';
8
+ import { _resetConfigCache, _setS3Fetcher } from './common/config.js';
8
9
  beforeEach(() => {
9
10
  clearRouteRegistry();
10
11
  });
@@ -22,6 +23,134 @@ async function invoke(backend, event) {
22
23
  const handler = createLambdaHandler(async () => backend);
23
24
  return handler(event);
24
25
  }
26
+ // ── init self-heal (retry on failed initialization) ─────────────────────────
27
+ describe('createLambdaHandler — init self-heal', () => {
28
+ it('retries initialize() on a later request instead of caching a failed init', async () => {
29
+ let initCalls = 0;
30
+ const backend = {
31
+ api: (_ctx) => ({
32
+ async echo(msg) {
33
+ return { msg };
34
+ },
35
+ }),
36
+ };
37
+ // Fail the first initialization (e.g. config not readable yet in the brief post-deploy
38
+ // window), then succeed. The SAME handler instance must recover — a cached rejected
39
+ // initPromise would poison the container and fail every subsequent request.
40
+ const backendFactory = async () => {
41
+ initCalls++;
42
+ if (initCalls === 1)
43
+ throw new Error('transient init failure');
44
+ return backend;
45
+ };
46
+ const handler = createLambdaHandler(backendFactory);
47
+ // 1st request: init fails, so the handler rejects.
48
+ await assert.rejects(() => handler(makeEvent()), /transient init failure/);
49
+ // 2nd request: init is retried and succeeds → 200 (not a re-thrown cached rejection).
50
+ const res = (await handler(makeEvent()));
51
+ assert.strictEqual(res.statusCode, 200);
52
+ assert.strictEqual(initCalls, 2, 'initialize() must be retried on the next request, not cached');
53
+ });
54
+ it('recovers from a transient-empty (post-deploy 404) config load on the next request', async () => {
55
+ // Reproduces the poisoned-container bug: the first S3 load hits the transient
56
+ // post-deploy window (NoSuchKey), so config resolves empty; the handler must
57
+ // NOT lock in that empty config, and the NEXT request must re-fetch and pick
58
+ // up the now-present config.
59
+ _resetConfigCache();
60
+ process.env.BLOCKS_CONFIG_BUCKET = 'test-bucket';
61
+ process.env.BLOCKS_CONFIG_KEY = 'blocks-config.json';
62
+ delete process.env.SELFHEAL_KEY;
63
+ let fetchCall = 0;
64
+ const notFound = new Error('The specified key does not exist.');
65
+ notFound.name = 'NoSuchKey';
66
+ _setS3Fetcher(async () => {
67
+ fetchCall++;
68
+ if (fetchCall === 1)
69
+ throw notFound; // config not readable yet
70
+ return JSON.stringify({ SELFHEAL_KEY: 'ready' });
71
+ });
72
+ let backendImports = 0;
73
+ const handler = createLambdaHandler(async () => {
74
+ backendImports++;
75
+ return {
76
+ api: (_ctx) => ({
77
+ async echo(msg) { return { msg, cfg: process.env.SELFHEAL_KEY }; },
78
+ }),
79
+ };
80
+ });
81
+ try {
82
+ // 1st request: transient-empty load → initialize() throws TransientConfigError
83
+ // BEFORE importing the backend (so we never lock in empty config), and the
84
+ // request rejects.
85
+ await assert.rejects(() => handler(makeEvent()), TransientConfigError);
86
+ assert.strictEqual(backendImports, 0, 'backend must NOT be imported while config is unresolved');
87
+ // 2nd request: config now present → initialize() re-runs, re-fetches, and
88
+ // injects the config into process.env before importing the backend.
89
+ const res = (await handler(makeEvent()));
90
+ assert.strictEqual(res.statusCode, 200);
91
+ const body = JSON.parse(res.body);
92
+ assert.strictEqual(body.result.cfg, 'ready', 'now-present config was injected on the retry');
93
+ assert.strictEqual(backendImports, 1, 'backend imported exactly once, on the successful retry');
94
+ assert.strictEqual(fetchCall, 2, 'S3 was re-fetched on the retry (transient miss is not cached)');
95
+ }
96
+ finally {
97
+ _resetConfigCache();
98
+ delete process.env.BLOCKS_CONFIG_BUCKET;
99
+ delete process.env.BLOCKS_CONFIG_KEY;
100
+ delete process.env.SELFHEAL_KEY;
101
+ }
102
+ });
103
+ it('does NOT re-init for a config-less app (no bucket, local dev) — initialize runs once', async () => {
104
+ _resetConfigCache();
105
+ delete process.env.BLOCKS_CONFIG_BUCKET;
106
+ delete process.env.BLOCKS_CONFIG_KEY;
107
+ let backendImports = 0;
108
+ const handler = createLambdaHandler(async () => {
109
+ backendImports++;
110
+ return { api: (_ctx) => ({ async echo(msg) { return { msg }; } }) };
111
+ });
112
+ try {
113
+ const r1 = (await handler(makeEvent()));
114
+ const r2 = (await handler(makeEvent()));
115
+ assert.strictEqual(r1.statusCode, 200);
116
+ assert.strictEqual(r2.statusCode, 200);
117
+ assert.strictEqual(backendImports, 1, 'no re-init for a genuinely config-less (local dev) app');
118
+ }
119
+ finally {
120
+ _resetConfigCache();
121
+ }
122
+ });
123
+ it('does NOT re-init or spin for a genuinely-empty ({}) S3 config', async () => {
124
+ // A real, readable empty config must be treated as resolved — distinct from
125
+ // the transient 404 miss — so the container does not throw/retry forever nor
126
+ // re-fetch S3 on every request.
127
+ _resetConfigCache();
128
+ process.env.BLOCKS_CONFIG_BUCKET = 'test-bucket';
129
+ process.env.BLOCKS_CONFIG_KEY = 'blocks-config.json';
130
+ let fetchCall = 0;
131
+ _setS3Fetcher(async () => { fetchCall++; return JSON.stringify({}); });
132
+ let backendImports = 0;
133
+ const handler = createLambdaHandler(async () => {
134
+ backendImports++;
135
+ return { api: (_ctx) => ({ async echo(msg) { return { msg }; } }) };
136
+ });
137
+ try {
138
+ const r1 = (await handler(makeEvent()));
139
+ const r2 = (await handler(makeEvent()));
140
+ const r3 = (await handler(makeEvent()));
141
+ assert.strictEqual(r1.statusCode, 200);
142
+ assert.strictEqual(r2.statusCode, 200);
143
+ assert.strictEqual(r3.statusCode, 200);
144
+ assert.strictEqual(backendImports, 1, 'genuinely-empty config resolves; no re-init');
145
+ assert.strictEqual(fetchCall, 1, 'S3 fetched once and cached — no per-request re-fetch spin');
146
+ }
147
+ finally {
148
+ _resetConfigCache();
149
+ delete process.env.BLOCKS_CONFIG_BUCKET;
150
+ delete process.env.BLOCKS_CONFIG_KEY;
151
+ }
152
+ });
153
+ });
25
154
  // ── RPC body tests ──────────────────────────────────────────────────────────
26
155
  describe('createLambdaHandler — RPC body handling', () => {
27
156
  it('populates ctx.request.body as a ReadableStream for RPC calls', async () => {
@@ -890,3 +1019,69 @@ describe('createLambdaHandler — sandbox forwarded-host reaches the route', ()
890
1019
  assert.strictEqual(capturedHost, 'abc123.execute-api.us-east-1.amazonaws.com');
891
1020
  });
892
1021
  });
1022
+ // ── SQS partial batch failure tests ─────────────────────────────────────────
1023
+ /**
1024
+ * These pin the runtime half of the bb-async-job BatchSize fix: with a batch
1025
+ * larger than 1, failing records must be reported individually via
1026
+ * `batchItemFailures` so SQS retries only those messages. If this response
1027
+ * shape regresses, every successful sibling message in a partially-failing
1028
+ * batch would be redelivered (or, worse, the failures silently dropped).
1029
+ */
1030
+ function makeSqsBatchEvent(count) {
1031
+ return {
1032
+ Records: Array.from({ length: count }, (_, i) => ({
1033
+ eventSource: 'aws:sqs',
1034
+ eventSourceARN: 'arn:aws:sqs:us-east-1:123456789012:my-queue',
1035
+ messageId: `msg-${i}`,
1036
+ body: JSON.stringify({ n: i }),
1037
+ })),
1038
+ };
1039
+ }
1040
+ describe('handleEventSourceRecords — SQS partial batch responses', () => {
1041
+ it('reports only the failed records of a mixed-success 10-record batch', async () => {
1042
+ const processed = [];
1043
+ const handlers = new Map();
1044
+ handlers.set('aws:sqs:my-queue', async (record) => {
1045
+ const { n } = JSON.parse(record.body);
1046
+ if (n % 3 === 0)
1047
+ throw new Error(`boom on ${n}`);
1048
+ processed.push(n);
1049
+ });
1050
+ globalThis.__BLOCKS_LAMBDA_EVENT_HANDLERS__ = handlers;
1051
+ try {
1052
+ const handler = createLambdaHandler(async () => ({}));
1053
+ const result = await handler(makeSqsBatchEvent(10));
1054
+ assert.deepStrictEqual(result.batchItemFailures.map((f) => f.itemIdentifier).sort(), ['msg-0', 'msg-3', 'msg-6', 'msg-9']);
1055
+ assert.deepStrictEqual(processed.sort((a, b) => a - b), [1, 2, 4, 5, 7, 8]);
1056
+ }
1057
+ finally {
1058
+ delete globalThis.__BLOCKS_LAMBDA_EVENT_HANDLERS__;
1059
+ }
1060
+ });
1061
+ it('returns no batchItemFailures when every record in the batch succeeds', async () => {
1062
+ const handlers = new Map();
1063
+ handlers.set('aws:sqs:my-queue', async () => { });
1064
+ globalThis.__BLOCKS_LAMBDA_EVENT_HANDLERS__ = handlers;
1065
+ try {
1066
+ const handler = createLambdaHandler(async () => ({}));
1067
+ const result = await handler(makeSqsBatchEvent(10));
1068
+ assert.deepStrictEqual(result, {});
1069
+ }
1070
+ finally {
1071
+ delete globalThis.__BLOCKS_LAMBDA_EVENT_HANDLERS__;
1072
+ }
1073
+ });
1074
+ it('reports all records when the whole batch fails (no silent drop)', async () => {
1075
+ const handlers = new Map();
1076
+ handlers.set('aws:sqs:my-queue', async () => { throw new Error('always'); });
1077
+ globalThis.__BLOCKS_LAMBDA_EVENT_HANDLERS__ = handlers;
1078
+ try {
1079
+ const handler = createLambdaHandler(async () => ({}));
1080
+ const result = await handler(makeSqsBatchEvent(10));
1081
+ assert.strictEqual(result.batchItemFailures.length, 10);
1082
+ }
1083
+ finally {
1084
+ delete globalThis.__BLOCKS_LAMBDA_EVENT_HANDLERS__;
1085
+ }
1086
+ });
1087
+ });
@@ -1,3 +1,25 @@
1
- export { Pipeline, DeployStage, __PIPELINE_STAGE_SCOPE__, } from '@aws-blocks/pipeline';
2
- export type { DeployStageProps, BranchConfig, PipelineProps, PipelineSourceConfig, PipelineSynthConfig, PipelineStageConfig, } from '@aws-blocks/pipeline';
1
+ import { __PIPELINE_STAGE_SCOPE__, DeployStage, Pipeline as LeafPipeline } from '@aws-blocks/pipeline';
2
+ import type { BranchConfig, DeployStageProps, PipelineProps, PipelineSourceConfig, PipelineStageConfig, PipelineSynthConfig } from '@aws-blocks/pipeline';
3
+ import type { Construct } from 'constructs';
4
+ export { __PIPELINE_STAGE_SCOPE__, DeployStage };
5
+ export type { BranchConfig, DeployStageProps, PipelineProps, PipelineSourceConfig, PipelineStageConfig, PipelineSynthConfig, };
6
+ /**
7
+ * Blocks `Pipeline` — the framework-neutral `@aws-blocks/pipeline` construct with
8
+ * the Blocks value namespace pinned. It defaults `secretStore` / `configStore` to
9
+ * `/blocks/secrets` and `/blocks/config` (the prefixes the Blocks `secret` /
10
+ * `config` CLIs write), mirroring how the Blocks `Hosting` block pins the same
11
+ * namespace — so a value set with `npm run secret`/`config` is the value the
12
+ * pipeline reads. Pass `secretStore` / `configStore` to override.
13
+ *
14
+ * @see {@link https://github.com/aws-devtools-labs/aws-blocks/blob/main/packages/pipeline/README.md}
15
+ */
16
+ export declare class Pipeline<TConfig = Record<string, unknown>> extends LeafPipeline<TConfig> {
17
+ constructor(scope: Construct, id: string, props: PipelineProps<TConfig>);
18
+ /**
19
+ * Async constructor — required when `source.connectionArn` is a `config()`
20
+ * marker (resolved at synth time). Pins the Blocks namespace, then delegates to
21
+ * the leaf {@link LeafPipeline.create}.
22
+ */
23
+ static create<TConfig = Record<string, unknown>>(scope: Construct, id: string, props: PipelineProps<TConfig>): Promise<LeafPipeline<TConfig>>;
24
+ }
3
25
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pipeline/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,QAAQ,EACR,WAAW,EACX,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pipeline/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACvG,OAAO,KAAK,EACX,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,CAAC;AACjD,YAAY,EACX,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,GACnB,CAAC;AAkBF;;;;;;;;;GASG;AACH,qBAAa,QAAQ,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAE,SAAQ,YAAY,CAAC,OAAO,CAAC;gBACzE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC;IAIvE;;;;OAIG;WACU,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpD,KAAK,EAAE,SAAS,EAChB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,GAC3B,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;CAGjC"}
@@ -1,3 +1,43 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- export { Pipeline, DeployStage, __PIPELINE_STAGE_SCOPE__, } from '@aws-blocks/pipeline';
3
+ import { __PIPELINE_STAGE_SCOPE__, DeployStage, Pipeline as LeafPipeline } from '@aws-blocks/pipeline';
4
+ import { blocksStoreConfig } from '../secret-naming.js';
5
+ export { __PIPELINE_STAGE_SCOPE__, DeployStage };
6
+ /**
7
+ * Merge the Blocks `/blocks/*` namespace as the DEFAULT store config, so a
8
+ * Blocks pipeline resolves `connectionArn` / `buildSecrets` from the same place
9
+ * the Blocks CLI (`npm run secret` / `npm run config`) writes. Per-kind and
10
+ * shallow: any field the caller sets (a custom `prefix`, a `stage`, a
11
+ * `cacheTtlSeconds`) wins over the Blocks default.
12
+ */
13
+ function withBlocksDefaults(props) {
14
+ const blocks = blocksStoreConfig();
15
+ return {
16
+ ...props,
17
+ secretStore: { ...blocks.secretStore, ...props.secretStore },
18
+ configStore: { ...blocks.configStore, ...props.configStore },
19
+ };
20
+ }
21
+ /**
22
+ * Blocks `Pipeline` — the framework-neutral `@aws-blocks/pipeline` construct with
23
+ * the Blocks value namespace pinned. It defaults `secretStore` / `configStore` to
24
+ * `/blocks/secrets` and `/blocks/config` (the prefixes the Blocks `secret` /
25
+ * `config` CLIs write), mirroring how the Blocks `Hosting` block pins the same
26
+ * namespace — so a value set with `npm run secret`/`config` is the value the
27
+ * pipeline reads. Pass `secretStore` / `configStore` to override.
28
+ *
29
+ * @see {@link https://github.com/aws-devtools-labs/aws-blocks/blob/main/packages/pipeline/README.md}
30
+ */
31
+ export class Pipeline extends LeafPipeline {
32
+ constructor(scope, id, props) {
33
+ super(scope, id, withBlocksDefaults(props));
34
+ }
35
+ /**
36
+ * Async constructor — required when `source.connectionArn` is a `config()`
37
+ * marker (resolved at synth time). Pins the Blocks namespace, then delegates to
38
+ * the leaf {@link LeafPipeline.create}.
39
+ */
40
+ static async create(scope, id, props) {
41
+ return LeafPipeline.create(scope, id, withBlocksDefaults(props));
42
+ }
43
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pipeline.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.test.d.ts","sourceRoot":"","sources":["../src/pipeline.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,47 @@
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 { afterEach, describe, it } from 'node:test';
5
+ import { _setSynthSecretFetcher } from '@aws-blocks/hosting/constructs';
6
+ import { config } from '@aws-blocks/hosting';
7
+ import { App, Stack } from 'aws-cdk-lib';
8
+ import { Pipeline } from './pipeline/index.js';
9
+ const MOCK_CONNECTION_ARN = 'arn:aws:codeconnections:us-east-1:123456789012:connection/test-connection-id';
10
+ function minimalStageFactory(scope, stageConfig) {
11
+ new Stack(scope, 'AppStack', { env: stageConfig.env });
12
+ }
13
+ // B2: the core Pipeline wrapper must resolve values from the SAME namespace the
14
+ // Blocks CLI writes (`/blocks/*`), not the leaf-neutral `/hosting/*` — otherwise
15
+ // `npm run config -- set CONNECTION_ARN` writes one name and the deploy reads
16
+ // another. The leaf default is `/hosting/config`; the wrapper must pin `/blocks/config`.
17
+ void describe('core Pipeline pins the Blocks namespace', () => {
18
+ afterEach(() => _setSynthSecretFetcher(null));
19
+ void it('resolves a config() connectionArn under /blocks/config by default', async () => {
20
+ const seen = [];
21
+ _setSynthSecretFetcher(async (locator) => {
22
+ seen.push(locator);
23
+ return MOCK_CONNECTION_ARN;
24
+ });
25
+ await Pipeline.create(new App(), 'BlocksPipeline', {
26
+ source: { repo: 'my-org/my-app', connectionArn: config('CONNECTION_ARN') },
27
+ branches: [{ branch: 'main', stages: [{ name: 'prod' }] }],
28
+ stageFactory: minimalStageFactory,
29
+ });
30
+ // The Blocks CLI writes /blocks/config/CONNECTION_ARN; the wrapper must read it there.
31
+ assert.deepStrictEqual(seen, ['/blocks/config/CONNECTION_ARN']);
32
+ });
33
+ void it('lets the caller override the pinned prefix', async () => {
34
+ const seen = [];
35
+ _setSynthSecretFetcher(async (locator) => {
36
+ seen.push(locator);
37
+ return MOCK_CONNECTION_ARN;
38
+ });
39
+ await Pipeline.create(new App(), 'OverridePipeline', {
40
+ source: { repo: 'my-org/my-app', connectionArn: config('CONNECTION_ARN') },
41
+ branches: [{ branch: 'main', stages: [{ name: 'prod' }] }],
42
+ stageFactory: minimalStageFactory,
43
+ configStore: { prefix: '/myapp/config' },
44
+ });
45
+ assert.deepStrictEqual(seen, ['/myapp/config/CONNECTION_ARN']);
46
+ });
47
+ });
package/dist/rpc.d.ts CHANGED
@@ -14,6 +14,27 @@ export type RpcParseResult = {
14
14
  ok: false;
15
15
  response: string;
16
16
  };
17
+ /**
18
+ * Maximum accepted request-body size, in bytes (10 MiB).
19
+ *
20
+ * Matches the payload limit API Gateway enforces in production (~10 MB for a
21
+ * REST API). In prod an oversized body is rejected by API Gateway at the edge —
22
+ * before the Lambda is invoked — so this guard's rejection path effectively
23
+ * runs on the dev/mock server, where there is no edge to stop it: an oversized
24
+ * body would otherwise buffer and wedge the local database (e.g. PGlite). By
25
+ * enforcing the *same* limit locally, the dev server rejects the same oversized
26
+ * body prod would 413 at the edge, instead of failing only in one environment.
27
+ * Enforced in `parseRpcRequest`, the single choke point both the Lambda handler
28
+ * and the dev server route through.
29
+ *
30
+ * Note: this value is the Lambda + API Gateway limit. It lives here (in the
31
+ * compute-agnostic parser) because Lambda is the only compute path today. When
32
+ * a non-API-Gateway compute (e.g. container/ALB) is introduced, this should
33
+ * become compute-aware — sourced from / overridden by the compute layer (via
34
+ * the Compute `setEnv` config hook) rather than a fixed constant in `core` —
35
+ * since an ALB has a different payload limit. Tracked with the multi-compute work.
36
+ */
37
+ export declare const MAX_RPC_BODY_BYTES: number;
17
38
  /** Reserved JSON-RPC error codes. */
18
39
  export declare const RpcErrorCode: {
19
40
  readonly ParseError: -32700;
package/dist/rpc.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"AAgBA,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,yDAAyD;AACzD,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,GACvC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAMpC,qCAAqC;AACrC,eAAO,MAAM,YAAY;;;;;;CAMf,CAAC;AAMX,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAO9F;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAiBxD;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CA0DhE;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAEnF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAOzF;AAED,yCAAyC;AACzC,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAEzF;AAID,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,EAC1B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAMR"}
1
+ {"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"AAgBA,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,yDAAyD;AACzD,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,GACvC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAMpC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,kBAAkB,QAAmB,CAAC;AAEnD,qCAAqC;AACrC,eAAO,MAAM,YAAY;;;;;;CAMf,CAAC;AAMX,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAO9F;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAiBxD;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CA6EhE;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAEnF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAOzF;AAED,yCAAyC;AACzC,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAEzF;AAID,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,EAC1B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAMR"}
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 || '{}');
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', () => {
@@ -154,3 +154,40 @@ describe('ApiError status ↔ JSON-RPC error code', () => {
154
154
  assert.throws(() => decodeRpcResponse({ jsonrpc: '2.0', error: { code: RpcErrorCode.InvalidRequest, message: 'Invalid Request' }, id: null }), (e) => e instanceof ApiError && e.status === 500);
155
155
  });
156
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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"}