@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.
- package/README.md +13 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +32 -0
- package/dist/api.test.js +22 -0
- package/dist/bb-utils.d.ts +1 -0
- package/dist/bb-utils.d.ts.map +1 -1
- package/dist/bb-utils.js +3 -0
- package/dist/cdk/apigateway-account.d.ts +33 -0
- package/dist/cdk/apigateway-account.d.ts.map +1 -0
- package/dist/cdk/apigateway-account.js +60 -0
- package/dist/cdk/blocks-backend.d.ts +43 -9
- package/dist/cdk/blocks-backend.d.ts.map +1 -1
- package/dist/cdk/blocks-backend.js +69 -73
- package/dist/cdk/blocks-backend.test.js +78 -69
- package/dist/cdk/blocks-defaults.d.ts +92 -2
- package/dist/cdk/blocks-defaults.d.ts.map +1 -1
- package/dist/cdk/blocks-defaults.js +22 -2
- package/dist/cdk/blocks-defaults.test.js +21 -2
- package/dist/cdk/blocks-stack.test.js +94 -37
- package/dist/cdk/compute/compute-registry.d.ts +19 -0
- package/dist/cdk/compute/compute-registry.d.ts.map +1 -0
- package/dist/cdk/compute/compute-registry.js +38 -0
- package/dist/cdk/compute/compute.d.ts +2 -0
- package/dist/cdk/compute/compute.d.ts.map +1 -1
- package/dist/cdk/compute/compute.js +8 -0
- package/dist/cdk/compute/default-compute-factory.d.ts +34 -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/config-registry.d.ts +34 -4
- package/dist/cdk/config-registry.d.ts.map +1 -1
- package/dist/cdk/config-registry.js +83 -25
- package/dist/cdk/config-registry.test.d.ts +2 -0
- package/dist/cdk/config-registry.test.d.ts.map +1 -0
- package/dist/cdk/config-registry.test.js +115 -0
- package/dist/cdk/index.d.ts +76 -7
- package/dist/cdk/index.d.ts.map +1 -1
- package/dist/cdk/index.js +99 -9
- package/dist/cdk/internal.d.ts +1 -0
- package/dist/cdk/internal.d.ts.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/common/config.d.ts +34 -0
- package/dist/common/config.d.ts.map +1 -1
- package/dist/common/config.js +45 -3
- package/dist/common/config.test.js +19 -0
- package/dist/common/index.d.ts +8 -0
- package/dist/common/index.d.ts.map +1 -1
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -0
- 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 +97 -6
- package/dist/hosting.d.ts.map +1 -1
- package/dist/hosting.js +91 -23
- package/dist/hosting.test.js +182 -10
- package/dist/index.cdk.d.ts +13 -12
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +13 -9
- 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.d.ts +17 -0
- package/dist/lambda-handler.d.ts.map +1 -1
- package/dist/lambda-handler.js +52 -3
- package/dist/lambda-handler.test.js +196 -1
- 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 +34 -0
- package/dist/rpc.test.js +38 -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-empty-buckets.test.d.ts +2 -0
- package/dist/scripts/sandbox-empty-buckets.test.d.ts.map +1 -0
- package/dist/scripts/sandbox-empty-buckets.test.js +171 -0
- package/dist/scripts/sandbox.d.ts +106 -0
- package/dist/scripts/sandbox.d.ts.map +1 -1
- package/dist/scripts/sandbox.js +255 -40
- 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/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/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -1
- package/src/api.test.ts +25 -0
- package/src/api.ts +39 -0
- package/src/bb-utils.ts +3 -0
- package/src/cdk/apigateway-account.ts +66 -0
- package/src/cdk/blocks-backend.test.ts +291 -272
- package/src/cdk/blocks-backend.ts +88 -83
- package/src/cdk/blocks-defaults.test.ts +25 -2
- package/src/cdk/blocks-defaults.ts +94 -2
- package/src/cdk/blocks-stack.test.ts +186 -118
- package/src/cdk/compute/compute-registry.ts +45 -0
- package/src/cdk/compute/compute.ts +10 -0
- package/src/cdk/compute/default-compute-factory.ts +38 -0
- package/src/cdk/config-registry.test.ts +135 -0
- package/src/cdk/config-registry.ts +92 -34
- package/src/cdk/index.ts +129 -10
- package/src/cdk/internal.ts +1 -0
- package/src/client/index.ts +1 -1
- package/src/common/config.test.ts +21 -0
- package/src/common/config.ts +47 -3
- package/src/common/index.ts +8 -0
- package/src/errors.ts +21 -0
- package/src/hosting-secrets.test.ts +106 -0
- package/src/hosting-secrets.ts +106 -0
- package/src/hosting.test.ts +272 -63
- package/src/hosting.ts +200 -68
- package/src/index.cdk.ts +80 -38
- package/src/index.ts +35 -16
- package/src/lambda-handler.test.ts +215 -1
- package/src/lambda-handler.ts +54 -2
- package/src/pipeline/index.ts +64 -11
- package/src/pipeline.test.ts +55 -0
- package/src/rpc.test.ts +44 -1
- package/src/rpc.ts +41 -0
- 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-empty-buckets.test.ts +191 -0
- package/src/scripts/sandbox.ts +287 -39
- package/src/scripts/secret.test.ts +35 -0
- package/src/scripts/secret.ts +35 -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/version.ts +1 -1
|
@@ -2,19 +2,18 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import * as cdk from 'aws-cdk-lib';
|
|
5
|
-
import * as
|
|
6
|
-
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
|
|
5
|
+
import type * as apigateway from 'aws-cdk-lib/aws-apigateway';
|
|
7
6
|
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
8
7
|
import { CfnGroup } from 'aws-cdk-lib/aws-resourcegroups';
|
|
9
8
|
import { Construct } from 'constructs';
|
|
10
9
|
import { pathToFileURL } from 'node:url';
|
|
11
|
-
import { DEFAULT_NODE_RUNTIME } from './node-version.js';
|
|
12
|
-
import { blocksNodejsBundling } from './bundling.js';
|
|
13
10
|
import { addBlocksStackMetadata } from './stack-metadata.js';
|
|
14
11
|
import { finalizeConfigRegistry, registerConfig } from './config-registry.js';
|
|
15
12
|
import type { BlocksDefaults } from './blocks-defaults.js';
|
|
16
|
-
import { BLOCKS_NAMESPACE, BLOCKS_RPC_PREFIX } from '../constants.js';
|
|
17
13
|
import { registerBuiltinRoutes } from '../builtin-routes.js';
|
|
14
|
+
import type { Compute } from './compute/compute.js';
|
|
15
|
+
import { getComputes } from './compute/compute-registry.js';
|
|
16
|
+
import type { DefaultComputeFactory, LambdaShapedCompute } from './compute/default-compute-factory.js';
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* Validate that the Node.js process was started with `--conditions=cdk`.
|
|
@@ -44,6 +43,13 @@ export function assertCdkConditionActive(): void {
|
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Timeout of the shared Blocks handler Lambda, and therefore the ceiling on a
|
|
48
|
+
* single invocation. Exported because resources that feed the handler have to
|
|
49
|
+
* size their own timeouts against it (e.g. an SQS queue's visibility timeout).
|
|
50
|
+
*/
|
|
51
|
+
export const SHARED_HANDLER_TIMEOUT_SECONDS = 60 * 15;
|
|
52
|
+
|
|
47
53
|
export interface BlocksBackendProps {
|
|
48
54
|
backendHandlerPath: string;
|
|
49
55
|
backendCDKPath: string;
|
|
@@ -57,7 +63,23 @@ export interface BlocksBackendProps {
|
|
|
57
63
|
defaults: BlocksDefaults;
|
|
58
64
|
}
|
|
59
65
|
|
|
60
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Core's `create()` props: the public {@link BlocksBackendProps} plus the
|
|
68
|
+
* required `defaultComputeFactory`, spread on by the umbrella (`@aws-blocks/blocks`).
|
|
69
|
+
* Customers use {@link BlocksBackendProps} and never set the factory.
|
|
70
|
+
*
|
|
71
|
+
* @internal
|
|
72
|
+
*/
|
|
73
|
+
export interface CoreBlocksBackendProps extends BlocksBackendProps {
|
|
74
|
+
/** Builds the backend's default compute. Injected by `@aws-blocks/blocks`. */
|
|
75
|
+
defaultComputeFactory: DefaultComputeFactory;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Shared infra setup — provisions the stack-level resources that are NOT owned
|
|
80
|
+
* by a compute: the shared execution role, resource groups, and console-redirect
|
|
81
|
+
* routes.
|
|
82
|
+
*/
|
|
61
83
|
export function setupBlocksInfra(scope: Construct, props: BlocksBackendProps, id?: string) {
|
|
62
84
|
// Fail fast with an actionable message at the create() call site if `defaults`
|
|
63
85
|
// is missing (e.g. a plain-JS caller, `as any`, or a dynamically-built props
|
|
@@ -70,79 +92,31 @@ export function setupBlocksInfra(scope: Construct, props: BlocksBackendProps, id
|
|
|
70
92
|
);
|
|
71
93
|
}
|
|
72
94
|
|
|
73
|
-
// ── Shared execution role
|
|
95
|
+
// ── Shared execution role ───────────────────────────────────────────────
|
|
74
96
|
// A single IAM role that every Building Block grants to. Provisioned here so
|
|
75
97
|
// it exists before the backend module is imported (Building Blocks reach it
|
|
76
98
|
// via `scope.executionRole`). Block grants sit on the role's default (inline)
|
|
77
|
-
// policy. AWSLambdaBasicExecutionRole is attached so
|
|
99
|
+
// policy. AWSLambdaBasicExecutionRole is attached so compute functions retain
|
|
78
100
|
// CloudWatch Logs permissions.
|
|
101
|
+
//
|
|
102
|
+
// INVARIANT: this must be a mutable, framework-owned `iam.Role` — never an
|
|
103
|
+
// imported role (`Role.fromRoleArn`/`fromRoleName`), which is immutable by
|
|
104
|
+
// default. On an immutable role, every Building Block's `grant*()` /
|
|
105
|
+
// `addToPrincipalPolicy()` silently becomes a no-op (returns false, no error),
|
|
106
|
+
// so permissions would quietly vanish. If a bring-your-own-role option is ever
|
|
107
|
+
// added, it must resolve to a mutable role (`{ mutable: true }`).
|
|
79
108
|
const executionRole = new iam.Role(scope, 'BlocksRole', {
|
|
80
|
-
// CompositePrincipal (rather than a bare ServicePrincipal) so
|
|
81
|
-
// compute
|
|
82
|
-
// (e.g.
|
|
109
|
+
// CompositePrincipal (rather than a bare ServicePrincipal) so a Building Block
|
|
110
|
+
// whose compute runs AS this shared role can add its own trust principal here
|
|
111
|
+
// (e.g. the Agent BB adds bedrock-agentcore in its CDK construct) — core stays
|
|
112
|
+
// agnostic and only Lambda is trusted by default.
|
|
83
113
|
assumedBy: new iam.CompositePrincipal(new iam.ServicePrincipal('lambda.amazonaws.com')),
|
|
84
114
|
managedPolicies: [
|
|
85
115
|
iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
|
|
86
116
|
],
|
|
87
117
|
});
|
|
88
118
|
|
|
89
|
-
|
|
90
|
-
entry: props.backendHandlerPath,
|
|
91
|
-
runtime: DEFAULT_NODE_RUNTIME,
|
|
92
|
-
handler: 'handler',
|
|
93
|
-
role: executionRole,
|
|
94
|
-
memorySize: 2048,
|
|
95
|
-
timeout: cdk.Duration.seconds(60 * 15),
|
|
96
|
-
environment: {
|
|
97
|
-
NODE_ENV: 'production',
|
|
98
|
-
/**
|
|
99
|
-
* BLOCKS_STACK_NAME is used at runtime to derive physical resource names
|
|
100
|
-
* (DynamoDB table names, env var prefixes). It must match the CDK-time
|
|
101
|
-
* fullId of the BlocksStack/BlocksBackend so resource lookups work correctly.
|
|
102
|
-
*
|
|
103
|
-
* For BlocksStack: this equals the stack name (id).
|
|
104
|
-
* For BlocksBackend: the caller overrides this after construction to include
|
|
105
|
-
* the parent stack name for deployment uniqueness.
|
|
106
|
-
*/
|
|
107
|
-
BLOCKS_STACK_NAME: id ?? cdk.Stack.of(scope).stackName,
|
|
108
|
-
},
|
|
109
|
-
// blocksNodejsBundling shims import.meta.* to CommonJS equivalents so a
|
|
110
|
-
// CJS-bundled `fileURLToPath(import.meta.url)` resolves instead of throwing at
|
|
111
|
-
// Lambda load. See ./bundling.ts.
|
|
112
|
-
bundling: blocksNodejsBundling({
|
|
113
|
-
minify: true,
|
|
114
|
-
esbuildArgs: { '--conditions': 'aws-runtime' },
|
|
115
|
-
}),
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// In sandbox mode, allow localhost origins so the local dev frontend can
|
|
119
|
-
// reach the deployed Lambda API via CORS.
|
|
120
|
-
const isSandbox =
|
|
121
|
-
scope.node.tryGetContext('sandboxMode') === 'true' ||
|
|
122
|
-
scope.node.tryGetContext('sandboxMode') === true;
|
|
123
|
-
if (isSandbox) {
|
|
124
|
-
handler.addEnvironment('CORS_ALLOWED_ORIGINS', '^https?://(localhost|127\\.0\\.0\\.1)(:\\d+)?$');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const api = new apigateway.RestApi(scope, 'API', {
|
|
128
|
-
restApiName: 'Blocks API',
|
|
129
|
-
deployOptions: { cachingEnabled: false },
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const integration = new apigateway.LambdaIntegration(handler);
|
|
133
|
-
|
|
134
|
-
// Build the nested resource tree for /aws-blocks/api.
|
|
135
|
-
// Intermediate resource gets a proxy so sub-paths (RawRoutes) still reach Lambda.
|
|
136
|
-
const awsBlocksResource = api.root.addResource(BLOCKS_NAMESPACE.slice(1));
|
|
137
|
-
awsBlocksResource.addProxy({ defaultIntegration: integration, anyMethod: true });
|
|
138
|
-
|
|
139
|
-
const apiResource = awsBlocksResource.addResource('api');
|
|
140
|
-
apiResource.addMethod('POST', integration);
|
|
141
|
-
apiResource.addMethod('OPTIONS', integration);
|
|
142
|
-
|
|
143
|
-
api.root.addProxy({ defaultIntegration: integration, anyMethod: true });
|
|
144
|
-
|
|
145
|
-
// ── Resource Groups ────────────────────────────────────────────────────
|
|
119
|
+
// ── Resource Groups ───────────────────────────────────────────────────
|
|
146
120
|
let rootStack = cdk.Stack.of(scope);
|
|
147
121
|
while (rootStack.nestedStackParent) rootStack = rootStack.nestedStackParent;
|
|
148
122
|
const groupPrefix = (id && id !== rootStack.stackName) ? `${rootStack.stackName}-${id}` : rootStack.stackName;
|
|
@@ -178,7 +152,7 @@ export function setupBlocksInfra(scope: Construct, props: BlocksBackendProps, id
|
|
|
178
152
|
},
|
|
179
153
|
});
|
|
180
154
|
|
|
181
|
-
// ── Console redirect routes
|
|
155
|
+
// ── Console redirect routes ───────────────────────────────────────────
|
|
182
156
|
const region = cdk.Fn.ref('AWS::Region');
|
|
183
157
|
const resourcesUrl = cdk.Fn.join('', [
|
|
184
158
|
'https://', region, '.console.aws.amazon.com/resource-groups/group/',
|
|
@@ -194,7 +168,7 @@ export function setupBlocksInfra(scope: Construct, props: BlocksBackendProps, id
|
|
|
194
168
|
|
|
195
169
|
registerBuiltinRoutes();
|
|
196
170
|
|
|
197
|
-
return {
|
|
171
|
+
return { executionRole };
|
|
198
172
|
}
|
|
199
173
|
|
|
200
174
|
/**
|
|
@@ -215,14 +189,43 @@ export function setupBlocksInfra(scope: Construct, props: BlocksBackendProps, id
|
|
|
215
189
|
* ```
|
|
216
190
|
*/
|
|
217
191
|
export class BlocksBackend extends Construct {
|
|
218
|
-
public readonly apiUrl: string;
|
|
219
|
-
public readonly gateway: apigateway.RestApi;
|
|
220
|
-
public readonly handler: cdk.aws_lambda_nodejs.NodejsFunction;
|
|
221
192
|
public readonly backendHandlerPath: string;
|
|
193
|
+
/**
|
|
194
|
+
* Path to the app's backend module (`props.backendCDKPath`). Exposed so Building Blocks that
|
|
195
|
+
* co-bundle the backend at synth (e.g. the Agent BB's AgentCore Runtime) can discover it via
|
|
196
|
+
* `globalThis.CURRENT_BLOCKS_STACK.backendModulePath`.
|
|
197
|
+
*/
|
|
198
|
+
public readonly backendModulePath: string;
|
|
222
199
|
/** Shared IAM role assumed by all Blocks compute. Building Blocks grant to this role. */
|
|
223
200
|
public readonly executionRole: iam.IRole;
|
|
224
201
|
/** Infrastructure defaults for Building Blocks created under this backend. */
|
|
225
202
|
public readonly defaults: BlocksDefaults;
|
|
203
|
+
/** The default compute (owns the Lambda function + API Gateway); set in `create()`. @internal */
|
|
204
|
+
_defaultCompute?: Compute;
|
|
205
|
+
|
|
206
|
+
/** The default compute's Lambda function. To be removed once consumers move to the multi-compute model. */
|
|
207
|
+
get handler(): cdk.aws_lambda_nodejs.NodejsFunction {
|
|
208
|
+
return this.requireDefaultCompute().fn;
|
|
209
|
+
}
|
|
210
|
+
/** The default compute's API Gateway REST API. To be removed once consumers move to the multi-compute model. */
|
|
211
|
+
get gateway(): apigateway.RestApi {
|
|
212
|
+
return this.requireDefaultCompute().apiGateway;
|
|
213
|
+
}
|
|
214
|
+
/** The default compute's RPC endpoint URL. To be removed once consumers move to the multi-compute model. */
|
|
215
|
+
get apiUrl(): string {
|
|
216
|
+
return this.requireDefaultCompute().apiUrl;
|
|
217
|
+
}
|
|
218
|
+
/** The default compute's handler CloudWatch log group. `bb-logger` reconfigures its retention. */
|
|
219
|
+
get handlerLogGroup(): cdk.aws_logs.ILogGroup {
|
|
220
|
+
return this.requireDefaultCompute().logGroup;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private requireDefaultCompute(): LambdaShapedCompute {
|
|
224
|
+
if (!this._defaultCompute) {
|
|
225
|
+
throw new Error('Blocks backend not fully initialized — access .handler/.gateway/.apiUrl after BlocksBackend.create() resolves.');
|
|
226
|
+
}
|
|
227
|
+
return this._defaultCompute as LambdaShapedCompute;
|
|
228
|
+
}
|
|
226
229
|
|
|
227
230
|
/**
|
|
228
231
|
* The fullId used by child Scopes to compute their env var names,
|
|
@@ -259,6 +262,7 @@ export class BlocksBackend extends Construct {
|
|
|
259
262
|
super(scope, id);
|
|
260
263
|
|
|
261
264
|
this.backendHandlerPath = props.backendHandlerPath;
|
|
265
|
+
this.backendModulePath = props.backendCDKPath;
|
|
262
266
|
|
|
263
267
|
// Expose self to Building Blocks at CDK time
|
|
264
268
|
(globalThis as any).CURRENT_BLOCKS_STACK = this;
|
|
@@ -269,20 +273,21 @@ export class BlocksBackend extends Construct {
|
|
|
269
273
|
this.defaults = props.defaults;
|
|
270
274
|
|
|
271
275
|
const infra = setupBlocksInfra(this, props, id);
|
|
272
|
-
this.handler = infra.handler;
|
|
273
|
-
this.gateway = infra.gateway;
|
|
274
|
-
this.apiUrl = infra.apiUrl;
|
|
275
276
|
this.executionRole = infra.executionRole;
|
|
276
|
-
|
|
277
|
-
//
|
|
278
|
-
// resource lookups (DynamoDB table names) match the CDK-time fullId
|
|
279
|
-
// and are unique per deployment.
|
|
280
|
-
this.handler.addEnvironment('BLOCKS_STACK_NAME', this.fullId);
|
|
277
|
+
// The default compute (and thus handler/gateway) is created in create(),
|
|
278
|
+
// after construction — it derives BLOCKS_STACK_NAME from this.fullId.
|
|
281
279
|
}
|
|
282
280
|
|
|
283
|
-
static async create(scope: Construct, id: string, props:
|
|
281
|
+
static async create(scope: Construct, id: string, props: CoreBlocksBackendProps) {
|
|
284
282
|
assertCdkConditionActive();
|
|
285
283
|
const backend = new BlocksBackend(scope, id, props);
|
|
284
|
+
// Create the default compute before importing the backend: it OWNS the
|
|
285
|
+
// Lambda function + API Gateway (which back .handler/.gateway/.apiUrl), and
|
|
286
|
+
// a block reading `this.compute` in its constructor (during that import)
|
|
287
|
+
// must resolve to it. The factory is supplied by the umbrella
|
|
288
|
+
// @aws-blocks/blocks (which injects LambdaCompute) via props, so core never
|
|
289
|
+
// imports the concrete compute class.
|
|
290
|
+
backend._defaultCompute = props.defaultComputeFactory(backend);
|
|
286
291
|
// file:// URL (not a raw path) so the cache-busting query works on Windows,
|
|
287
292
|
// where an absolute path like `D:\...` is rejected as URL scheme `d:`.
|
|
288
293
|
const backendUrl = pathToFileURL(props.backendCDKPath);
|
|
@@ -298,7 +303,7 @@ export class BlocksBackend extends Construct {
|
|
|
298
303
|
addBlocksStackMetadata(cdk.Stack.of(backend));
|
|
299
304
|
|
|
300
305
|
// Finalize BB config → S3 (after all BBs have registered their config)
|
|
301
|
-
finalizeConfigRegistry(backend, backend.
|
|
306
|
+
finalizeConfigRegistry(backend, backend.executionRole, getComputes(backend));
|
|
302
307
|
|
|
303
308
|
return backend;
|
|
304
309
|
}
|
|
@@ -10,16 +10,39 @@
|
|
|
10
10
|
import { test, describe } from 'node:test';
|
|
11
11
|
import assert from 'node:assert';
|
|
12
12
|
import { RemovalPolicy } from 'aws-cdk-lib';
|
|
13
|
+
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
13
14
|
import { BlocksPresets } from './blocks-defaults.js';
|
|
14
15
|
|
|
15
16
|
describe('BlocksPresets', () => {
|
|
16
|
-
test('sandbox is disposable: DESTROY + deletion protection off', () => {
|
|
17
|
+
test('sandbox is disposable: DESTROY + deletion protection off + no PITR', () => {
|
|
17
18
|
assert.strictEqual(BlocksPresets.sandbox.removalPolicy, RemovalPolicy.DESTROY);
|
|
18
19
|
assert.strictEqual(BlocksPresets.sandbox.deletionProtection, false);
|
|
20
|
+
assert.strictEqual(BlocksPresets.sandbox.pointInTimeRecovery, false);
|
|
19
21
|
});
|
|
20
22
|
|
|
21
|
-
test('production is durable: RETAIN + deletion protection on', () => {
|
|
23
|
+
test('production is durable: RETAIN + deletion protection on + PITR on', () => {
|
|
22
24
|
assert.strictEqual(BlocksPresets.production.removalPolicy, RemovalPolicy.RETAIN);
|
|
23
25
|
assert.strictEqual(BlocksPresets.production.deletionProtection, true);
|
|
26
|
+
assert.strictEqual(BlocksPresets.production.pointInTimeRecovery, true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('sandbox keeps logs briefly', () => {
|
|
30
|
+
assert.strictEqual(BlocksPresets.sandbox.logRetention, RetentionDays.ONE_WEEK);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('production keeps logs a year', () => {
|
|
34
|
+
assert.strictEqual(BlocksPresets.production.logRetention, RetentionDays.ONE_YEAR);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('access logging is off by default in BOTH presets (opt-in; mutates an account singleton)', () => {
|
|
38
|
+
assert.strictEqual(BlocksPresets.sandbox.accessLogging, false);
|
|
39
|
+
assert.strictEqual(BlocksPresets.production.accessLogging, false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('sandbox caps tighter (200/400) than production (1000/2000)', () => {
|
|
43
|
+
assert.strictEqual(BlocksPresets.sandbox.throttling.rateLimit, 200);
|
|
44
|
+
assert.strictEqual(BlocksPresets.sandbox.throttling.burstLimit, 400);
|
|
45
|
+
assert.strictEqual(BlocksPresets.production.throttling.rateLimit, 1000);
|
|
46
|
+
assert.strictEqual(BlocksPresets.production.throttling.burstLimit, 2000);
|
|
24
47
|
});
|
|
25
48
|
});
|
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import { RemovalPolicy } from 'aws-cdk-lib';
|
|
5
|
+
import { RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Request-rate limits applied to an API Gateway stage. On a REST API these are
|
|
9
|
+
* requests/second; on a WebSocket API the unit is messages/second across the
|
|
10
|
+
* connection. `rateLimit` is the steady-state ceiling and `burstLimit` the
|
|
11
|
+
* token-bucket size for short spikes.
|
|
12
|
+
*/
|
|
13
|
+
export interface BlocksThrottling {
|
|
14
|
+
/** Steady-state request (or WebSocket message) rate ceiling, per second. */
|
|
15
|
+
rateLimit: number;
|
|
16
|
+
/** Token-bucket burst size for short spikes above `rateLimit`. */
|
|
17
|
+
burstLimit: number;
|
|
18
|
+
}
|
|
5
19
|
|
|
6
20
|
/**
|
|
7
21
|
* Default values for every Amazon-authored Building Block created within a
|
|
@@ -25,6 +39,65 @@ export interface BlocksDefaults {
|
|
|
25
39
|
* well as the CLI, API, and AWS console — until it is turned off.
|
|
26
40
|
*/
|
|
27
41
|
deletionProtection: boolean;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* CORS origins the compute's API accepts, as regular-expression patterns
|
|
45
|
+
* matched against the request `Origin` header. Empty means no cross-origin
|
|
46
|
+
* requests are allowed. The `sandbox` preset allows localhost so a local dev
|
|
47
|
+
* frontend can reach a deployed API; `production` allows none by default.
|
|
48
|
+
*/
|
|
49
|
+
allowedOrigins: string[];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Whether stateful resources that support continuous backups keep them on by
|
|
53
|
+
* default — e.g. DynamoDB Point-in-Time Recovery, letting you restore to any
|
|
54
|
+
* second in the retention window. On in `production`, off in `sandbox` (where
|
|
55
|
+
* throwaway data isn't worth the backup-storage cost). Blocks whose service
|
|
56
|
+
* has no equivalent simply ignore it.
|
|
57
|
+
*
|
|
58
|
+
* `true` enables backups with the service's default window; `false` disables
|
|
59
|
+
* them; `{ retentionDays: n }` enables them and pins the window (a block
|
|
60
|
+
* clamps/validates `n` to its service's supported range — DynamoDB PITR is
|
|
61
|
+
* 1–35 days). Backups only have a window when on, so the two are one field.
|
|
62
|
+
*/
|
|
63
|
+
pointInTimeRecovery: boolean | { retentionDays: number };
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* How long CloudWatch Logs keeps log events written by Blocks-managed log
|
|
67
|
+
* groups (the core handler Lambda, migration/GSI Lambdas, hosting compute,
|
|
68
|
+
* `bb-logger`, and API Gateway access logs) before expiring them. Without a
|
|
69
|
+
* retention set, AWS keeps log events forever, which grows cost unbounded —
|
|
70
|
+
* every Blocks-managed log group reads this default so retention is applied
|
|
71
|
+
* consistently.
|
|
72
|
+
*/
|
|
73
|
+
logRetention: RetentionDays;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Request-rate limits applied to every Blocks-managed API Gateway stage: the
|
|
77
|
+
* core REST API, the SSR/hosting REST API, and the `bb-realtime` WebSocket
|
|
78
|
+
* stage. Protects the backend from runaway clients and caps blast radius.
|
|
79
|
+
* The sandbox preset caps tighter (200/400) than production (1000/2000) so a
|
|
80
|
+
* disposable stack is well-protected without throttling real production
|
|
81
|
+
* traffic. See {@link BlocksThrottling}.
|
|
82
|
+
*/
|
|
83
|
+
throttling: BlocksThrottling;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Whether to emit structured JSON access logs from every Blocks-managed API
|
|
87
|
+
* Gateway stage to a dedicated CloudWatch log group (retention follows
|
|
88
|
+
* {@link logRetention}).
|
|
89
|
+
*
|
|
90
|
+
* **Off by default in both presets** — opt in with a per-stack override
|
|
91
|
+
* (`defaults: { ...BlocksPresets.production, accessLogging: true }`). It is
|
|
92
|
+
* off by default (rather than on for production) because enabling it
|
|
93
|
+
* provisions the account-level API Gateway CloudWatch Logs role, which is an
|
|
94
|
+
* account/region-level singleton: a second Blocks stack in the same
|
|
95
|
+
* account+region that also enables access logging can repoint that role on
|
|
96
|
+
* deploy or, on teardown, leave the survivor's access logging broken.
|
|
97
|
+
* Enabling it is therefore safe for **one Blocks stack per region** — see
|
|
98
|
+
* `ensureApiGatewayAccount` for the full multi-stack teardown caveat.
|
|
99
|
+
*/
|
|
100
|
+
accessLogging: boolean;
|
|
28
101
|
}
|
|
29
102
|
|
|
30
103
|
/**
|
|
@@ -36,14 +109,33 @@ export interface BlocksDefaults {
|
|
|
36
109
|
* ```
|
|
37
110
|
*/
|
|
38
111
|
export const BlocksPresets = {
|
|
39
|
-
/** Disposable development stacks: tear down cleanly, no delete guard. */
|
|
112
|
+
/** Disposable development stacks: tear down cleanly, no delete guard, no backups. */
|
|
40
113
|
sandbox: {
|
|
41
114
|
removalPolicy: RemovalPolicy.DESTROY,
|
|
42
115
|
deletionProtection: false,
|
|
116
|
+
allowedOrigins: ['^https?://(localhost|127\\.0\\.0\\.1)(:\\d+)?$'],
|
|
117
|
+
pointInTimeRecovery: false,
|
|
118
|
+
logRetention: RetentionDays.ONE_WEEK,
|
|
119
|
+
throttling: { rateLimit: 200, burstLimit: 400 },
|
|
120
|
+
accessLogging: false,
|
|
43
121
|
},
|
|
44
|
-
/**
|
|
122
|
+
/**
|
|
123
|
+
* Durable, protected posture for permanent deployments. A higher throttle
|
|
124
|
+
* ceiling (1000/2000) than sandbox so the default doesn't 429 real
|
|
125
|
+
* production traffic — raise it via a per-stack `throttling` override for
|
|
126
|
+
* higher-volume APIs.
|
|
127
|
+
*/
|
|
45
128
|
production: {
|
|
46
129
|
removalPolicy: RemovalPolicy.RETAIN,
|
|
47
130
|
deletionProtection: true,
|
|
131
|
+
allowedOrigins: [],
|
|
132
|
+
pointInTimeRecovery: true,
|
|
133
|
+
logRetention: RetentionDays.ONE_YEAR,
|
|
134
|
+
throttling: { rateLimit: 1000, burstLimit: 2000 },
|
|
135
|
+
// Off by default even in production: enabling access logging mutates the
|
|
136
|
+
// account/region-level API Gateway CloudWatch role (a singleton). Opt in
|
|
137
|
+
// per stack once you've confirmed a single Blocks stack owns it in the
|
|
138
|
+
// region — see the `accessLogging` field doc.
|
|
139
|
+
accessLogging: false,
|
|
48
140
|
},
|
|
49
141
|
} satisfies Record<string, BlocksDefaults>;
|