@aws-blocks/core 0.1.17 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdk/__fixtures__/import-meta-handler.d.ts +6 -0
- package/dist/cdk/__fixtures__/import-meta-handler.d.ts.map +1 -0
- package/dist/cdk/__fixtures__/import-meta-handler.js +13 -0
- package/dist/cdk/blocks-backend.d.ts +15 -0
- package/dist/cdk/blocks-backend.d.ts.map +1 -1
- package/dist/cdk/blocks-backend.js +41 -3
- package/dist/cdk/blocks-backend.test.js +137 -1
- package/dist/cdk/blocks-defaults.d.ts +44 -0
- package/dist/cdk/blocks-defaults.d.ts.map +1 -0
- package/dist/cdk/blocks-defaults.js +23 -0
- package/dist/cdk/blocks-defaults.test.d.ts +2 -0
- package/dist/cdk/blocks-defaults.test.d.ts.map +1 -0
- package/dist/cdk/blocks-defaults.test.js +22 -0
- package/dist/cdk/blocks-stack.test.js +39 -1
- package/dist/cdk/bundling.d.ts +42 -0
- package/dist/cdk/bundling.d.ts.map +1 -0
- package/dist/cdk/bundling.js +72 -0
- package/dist/cdk/bundling.test.d.ts +2 -0
- package/dist/cdk/bundling.test.d.ts.map +1 -0
- package/dist/cdk/bundling.test.js +80 -0
- package/dist/cdk/compute/compute.d.ts +33 -0
- package/dist/cdk/compute/compute.d.ts.map +1 -0
- package/dist/cdk/compute/compute.js +28 -0
- package/dist/cdk/index.d.ts +52 -0
- package/dist/cdk/index.d.ts.map +1 -1
- package/dist/cdk/index.js +95 -5
- package/dist/cdk/internal.d.ts +24 -0
- package/dist/cdk/internal.d.ts.map +1 -0
- package/dist/cdk/internal.js +27 -0
- package/dist/cdk/mixins.d.ts +18 -4
- package/dist/cdk/mixins.d.ts.map +1 -1
- package/dist/cdk/mixins.js +31 -7
- package/dist/cdk/mixins.test.js +45 -0
- package/dist/common/index.d.ts +9 -0
- package/dist/common/index.d.ts.map +1 -1
- package/dist/index.cdk.d.ts +2 -1
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +2 -1
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +12 -3
- package/dist/rpc.test.js +20 -0
- package/dist/scripts/telemetry.test.js +2 -0
- package/dist/telemetry/environment.d.ts.map +1 -1
- package/dist/telemetry/environment.js +3 -1
- package/dist/telemetry/telemetry.test.js +10 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -1
- package/src/cdk/__fixtures__/import-meta-handler.ts +16 -0
- package/src/cdk/blocks-backend.test.ts +166 -1
- package/src/cdk/blocks-backend.ts +55 -3
- package/src/cdk/blocks-defaults.test.ts +25 -0
- package/src/cdk/blocks-defaults.ts +49 -0
- package/src/cdk/blocks-stack.test.ts +48 -1
- package/src/cdk/bundling.test.ts +90 -0
- package/src/cdk/bundling.ts +76 -0
- package/src/cdk/compute/compute.ts +37 -0
- package/src/cdk/index.ts +105 -5
- package/src/cdk/internal.ts +29 -0
- package/src/cdk/mixins.test.ts +56 -1
- package/src/cdk/mixins.ts +32 -7
- package/src/common/index.ts +9 -0
- package/src/index.cdk.ts +5 -1
- package/src/rpc.test.ts +22 -0
- package/src/rpc.ts +19 -3
- package/src/scripts/telemetry.test.ts +2 -0
- package/src/telemetry/environment.ts +3 -1
- package/src/telemetry/telemetry.test.ts +12 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-meta-handler.d.ts","sourceRoot":"","sources":["../../../src/cdk/__fixtures__/import-meta-handler.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,SAAS,QAAiC,CAAC;AAGxD,eAAO,MAAM,OAAO;;;EAAyE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
// Repro fixture for the "handler bundles to CJS but uses import.meta.url" bug.
|
|
4
|
+
//
|
|
5
|
+
// The handler is bundled to CJS by NodejsFunction. In a CJS bundle `import.meta`
|
|
6
|
+
// is empty, so this top-level `fileURLToPath(import.meta.url)` would become
|
|
7
|
+
// `fileURLToPath(undefined)` and throw at Lambda load. blocksNodejsBundling shims
|
|
8
|
+
// `import.meta.url` to a CommonJS equivalent, so the bundle loads and resolves a
|
|
9
|
+
// real path instead of crashing — see bundling.test.ts.
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
export const moduleDir = fileURLToPath(import.meta.url);
|
|
12
|
+
// Reference moduleDir so esbuild can't tree-shake the import.meta.url usage away.
|
|
13
|
+
export const handler = async () => ({ statusCode: 200, body: JSON.stringify({ moduleDir }) });
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as cdk from 'aws-cdk-lib';
|
|
2
2
|
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
|
|
3
|
+
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
3
4
|
import { Construct } from 'constructs';
|
|
5
|
+
import type { BlocksDefaults } from './blocks-defaults.js';
|
|
4
6
|
/**
|
|
5
7
|
* Validate that the Node.js process was started with `--conditions=cdk`.
|
|
6
8
|
*
|
|
@@ -13,12 +15,21 @@ export declare function assertCdkConditionActive(): void;
|
|
|
13
15
|
export interface BlocksBackendProps {
|
|
14
16
|
backendHandlerPath: string;
|
|
15
17
|
backendCDKPath: string;
|
|
18
|
+
/**
|
|
19
|
+
* Stack-wide infrastructure defaults applied to every Building Block (removal
|
|
20
|
+
* policy, deletion protection, …). See {@link BlocksDefaults}. Start from
|
|
21
|
+
* `BlocksPresets.sandbox` or `BlocksPresets.production` and override
|
|
22
|
+
* individual fields as needed. A per-block option always wins over the
|
|
23
|
+
* corresponding stack default.
|
|
24
|
+
*/
|
|
25
|
+
defaults: BlocksDefaults;
|
|
16
26
|
}
|
|
17
27
|
/** Shared infra setup — creates Lambda + API Gateway on the given scope. */
|
|
18
28
|
export declare function setupBlocksInfra(scope: Construct, props: BlocksBackendProps, id?: string): {
|
|
19
29
|
handler: cdk.aws_lambda_nodejs.NodejsFunction;
|
|
20
30
|
gateway: cdk.aws_apigateway.RestApi;
|
|
21
31
|
apiUrl: string;
|
|
32
|
+
executionRole: cdk.aws_iam.Role;
|
|
22
33
|
};
|
|
23
34
|
/**
|
|
24
35
|
* Standalone CDK construct that provisions the Blocks backend: a single Lambda
|
|
@@ -42,6 +53,10 @@ export declare class BlocksBackend extends Construct {
|
|
|
42
53
|
readonly gateway: apigateway.RestApi;
|
|
43
54
|
readonly handler: cdk.aws_lambda_nodejs.NodejsFunction;
|
|
44
55
|
readonly backendHandlerPath: string;
|
|
56
|
+
/** Shared IAM role assumed by all Blocks compute. Building Blocks grant to this role. */
|
|
57
|
+
readonly executionRole: iam.IRole;
|
|
58
|
+
/** Infrastructure defaults for Building Blocks created under this backend. */
|
|
59
|
+
readonly defaults: BlocksDefaults;
|
|
45
60
|
/**
|
|
46
61
|
* The fullId used by child Scopes to compute their env var names,
|
|
47
62
|
* construct IDs, and physical resource names (e.g., DynamoDB table names).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blocks-backend.d.ts","sourceRoot":"","sources":["../../src/cdk/blocks-backend.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,GAAG,MAAM,aAAa,CAAC;AAEnC,OAAO,KAAK,UAAU,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"blocks-backend.d.ts","sourceRoot":"","sources":["../../src/cdk/blocks-backend.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,GAAG,MAAM,aAAa,CAAC;AAEnC,OAAO,KAAK,UAAU,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAMvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAkB/C;AAED,MAAM,WAAW,kBAAkB;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED,4EAA4E;AAC5E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,CAAC,EAAE,MAAM;;;;;EAyIxF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,aAAc,SAAQ,SAAS;IAC1C,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC;IAC5C,SAAgB,OAAO,EAAE,GAAG,CAAC,iBAAiB,CAAC,cAAc,CAAC;IAC9D,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAC3C,yFAAyF;IACzF,SAAgB,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC;IACzC,8EAA8E;IAC9E,SAAgB,QAAQ,EAAE,cAAc,CAAC;IAEzC;;;;;;;;;;;;;;;;;;OAkBG;IACH,IAAI,MAAM,IAAI,MAAM,CAUnB;IAED,OAAO;WAyBM,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB;CAsB5E"}
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
import * as cdk from 'aws-cdk-lib';
|
|
4
4
|
import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs';
|
|
5
5
|
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
|
|
6
|
+
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
6
7
|
import { CfnGroup } from 'aws-cdk-lib/aws-resourcegroups';
|
|
7
8
|
import { Construct } from 'constructs';
|
|
8
9
|
import { pathToFileURL } from 'node:url';
|
|
9
10
|
import { DEFAULT_NODE_RUNTIME } from './node-version.js';
|
|
11
|
+
import { blocksNodejsBundling } from './bundling.js';
|
|
10
12
|
import { addBlocksStackMetadata } from './stack-metadata.js';
|
|
11
13
|
import { finalizeConfigRegistry, registerConfig } from './config-registry.js';
|
|
12
14
|
import { BLOCKS_NAMESPACE, BLOCKS_RPC_PREFIX } from '../constants.js';
|
|
@@ -35,10 +37,34 @@ export function assertCdkConditionActive() {
|
|
|
35
37
|
}
|
|
36
38
|
/** Shared infra setup — creates Lambda + API Gateway on the given scope. */
|
|
37
39
|
export function setupBlocksInfra(scope, props, id) {
|
|
40
|
+
// Fail fast with an actionable message at the create() call site if `defaults`
|
|
41
|
+
// is missing (e.g. a plain-JS caller, `as any`, or a dynamically-built props
|
|
42
|
+
// object) — otherwise the first Building Block to read `scope.defaults` throws
|
|
43
|
+
// a cryptic `Cannot read properties of undefined (reading 'removalPolicy')`.
|
|
44
|
+
if (!props.defaults) {
|
|
45
|
+
throw new Error('BlocksStack/BlocksBackend requires a `defaults` field. Pass a posture from ' +
|
|
46
|
+
'`@aws-blocks/core/cdk` — typically `defaults: sandboxMode ? BlocksPresets.sandbox : BlocksPresets.production`.');
|
|
47
|
+
}
|
|
48
|
+
// ── Shared execution role ──────────────────────────────────────────────
|
|
49
|
+
// A single IAM role that every Building Block grants to. Provisioned here so
|
|
50
|
+
// it exists before the backend module is imported (Building Blocks reach it
|
|
51
|
+
// via `scope.executionRole`). Block grants sit on the role's default (inline)
|
|
52
|
+
// policy. AWSLambdaBasicExecutionRole is attached so the handler retains
|
|
53
|
+
// CloudWatch Logs permissions.
|
|
54
|
+
const executionRole = new iam.Role(scope, 'BlocksRole', {
|
|
55
|
+
// CompositePrincipal (rather than a bare ServicePrincipal) so additional
|
|
56
|
+
// compute types can assume this same shared role as they are introduced
|
|
57
|
+
// (e.g. ECS tasks via ecs-tasks.amazonaws.com), by adding principals here.
|
|
58
|
+
assumedBy: new iam.CompositePrincipal(new iam.ServicePrincipal('lambda.amazonaws.com')),
|
|
59
|
+
managedPolicies: [
|
|
60
|
+
iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
|
|
61
|
+
],
|
|
62
|
+
});
|
|
38
63
|
const handler = new lambda.NodejsFunction(scope, 'Handler', {
|
|
39
64
|
entry: props.backendHandlerPath,
|
|
40
65
|
runtime: DEFAULT_NODE_RUNTIME,
|
|
41
66
|
handler: 'handler',
|
|
67
|
+
role: executionRole,
|
|
42
68
|
memorySize: 2048,
|
|
43
69
|
timeout: cdk.Duration.seconds(60 * 15),
|
|
44
70
|
environment: {
|
|
@@ -54,10 +80,13 @@ export function setupBlocksInfra(scope, props, id) {
|
|
|
54
80
|
*/
|
|
55
81
|
BLOCKS_STACK_NAME: id ?? cdk.Stack.of(scope).stackName,
|
|
56
82
|
},
|
|
57
|
-
|
|
83
|
+
// blocksNodejsBundling shims import.meta.* to CommonJS equivalents so a
|
|
84
|
+
// CJS-bundled `fileURLToPath(import.meta.url)` resolves instead of throwing at
|
|
85
|
+
// Lambda load. See ./bundling.ts.
|
|
86
|
+
bundling: blocksNodejsBundling({
|
|
58
87
|
minify: true,
|
|
59
88
|
esbuildArgs: { '--conditions': 'aws-runtime' },
|
|
60
|
-
},
|
|
89
|
+
}),
|
|
61
90
|
});
|
|
62
91
|
// In sandbox mode, allow localhost origins so the local dev frontend can
|
|
63
92
|
// reach the deployed Lambda API via CORS.
|
|
@@ -126,7 +155,7 @@ export function setupBlocksInfra(scope, props, id) {
|
|
|
126
155
|
registerConfig(scope, 'BB_RESOURCES_GROUP_URL', resourcesUrl);
|
|
127
156
|
registerConfig(scope, 'BB_SETTINGS_GROUP_URL', settingsUrl);
|
|
128
157
|
registerBuiltinRoutes();
|
|
129
|
-
return { handler, gateway: api, apiUrl: `${api.url}${BLOCKS_RPC_PREFIX.slice(1)}
|
|
158
|
+
return { handler, gateway: api, apiUrl: `${api.url}${BLOCKS_RPC_PREFIX.slice(1)}`, executionRole };
|
|
130
159
|
}
|
|
131
160
|
/**
|
|
132
161
|
* Standalone CDK construct that provisions the Blocks backend: a single Lambda
|
|
@@ -150,6 +179,10 @@ export class BlocksBackend extends Construct {
|
|
|
150
179
|
gateway;
|
|
151
180
|
handler;
|
|
152
181
|
backendHandlerPath;
|
|
182
|
+
/** Shared IAM role assumed by all Blocks compute. Building Blocks grant to this role. */
|
|
183
|
+
executionRole;
|
|
184
|
+
/** Infrastructure defaults for Building Blocks created under this backend. */
|
|
185
|
+
defaults;
|
|
153
186
|
/**
|
|
154
187
|
* The fullId used by child Scopes to compute their env var names,
|
|
155
188
|
* construct IDs, and physical resource names (e.g., DynamoDB table names).
|
|
@@ -185,10 +218,15 @@ export class BlocksBackend extends Construct {
|
|
|
185
218
|
this.backendHandlerPath = props.backendHandlerPath;
|
|
186
219
|
// Expose self to Building Blocks at CDK time
|
|
187
220
|
globalThis.CURRENT_BLOCKS_STACK = this;
|
|
221
|
+
// Store defaults on the backend (not the stack) so several BlocksBackends
|
|
222
|
+
// in one stack each keep their own posture; Building Blocks resolve them by
|
|
223
|
+
// walking up to their owning backend (see Scope.defaults).
|
|
224
|
+
this.defaults = props.defaults;
|
|
188
225
|
const infra = setupBlocksInfra(this, props, id);
|
|
189
226
|
this.handler = infra.handler;
|
|
190
227
|
this.gateway = infra.gateway;
|
|
191
228
|
this.apiUrl = infra.apiUrl;
|
|
229
|
+
this.executionRole = infra.executionRole;
|
|
192
230
|
// Override BLOCKS_STACK_NAME to include the parent stack name so runtime
|
|
193
231
|
// resource lookups (DynamoDB table names) match the CDK-time fullId
|
|
194
232
|
// and are unique per deployment.
|
|
@@ -5,8 +5,11 @@ import assert from 'node:assert';
|
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { dirname, join } from 'node:path';
|
|
7
7
|
import * as cdk from 'aws-cdk-lib';
|
|
8
|
-
import { Template } from 'aws-cdk-lib/assertions';
|
|
8
|
+
import { Template, Match } from 'aws-cdk-lib/assertions';
|
|
9
|
+
import { PolicyStatement } from 'aws-cdk-lib/aws-iam';
|
|
9
10
|
import { BlocksBackend } from './blocks-backend.js';
|
|
11
|
+
import { BlocksPresets } from './blocks-defaults.js';
|
|
12
|
+
import { Scope } from './index.js';
|
|
10
13
|
// Simulate the CDK condition being active (tests import CDK files directly)
|
|
11
14
|
before(() => {
|
|
12
15
|
process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS ?? '') + ' --conditions=cdk';
|
|
@@ -16,6 +19,8 @@ const handlerPath = join(__dirname, '__fixtures__', 'handler.js');
|
|
|
16
19
|
const sideEffectBackendPath = join(__dirname, '__fixtures__', 'side-effect-backend.js');
|
|
17
20
|
const factoryBackendPath = join(__dirname, '__fixtures__', 'factory-backend.js');
|
|
18
21
|
const fullIdConstructBackendPath = join(__dirname, '__fixtures__', 'fullid-construct-backend.js');
|
|
22
|
+
const EXECUTION_ROLE_MARKER_ACTION = 'blocks-test:MarkerAction';
|
|
23
|
+
const importMetaHandlerPath = join(__dirname, '__fixtures__', 'import-meta-handler.js');
|
|
19
24
|
describe('ESM cache-busting (multi-stage)', () => {
|
|
20
25
|
test('BlocksBackend.create() with same backendCDKPath but different IDs produces constructs in each', async () => {
|
|
21
26
|
const app = new cdk.App();
|
|
@@ -23,10 +28,12 @@ describe('ESM cache-busting (multi-stage)', () => {
|
|
|
23
28
|
const backend1 = await BlocksBackend.create(stack, 'Stage1', {
|
|
24
29
|
backendHandlerPath: handlerPath,
|
|
25
30
|
backendCDKPath: sideEffectBackendPath,
|
|
31
|
+
defaults: BlocksPresets.production,
|
|
26
32
|
});
|
|
27
33
|
const backend2 = await BlocksBackend.create(stack, 'Stage2', {
|
|
28
34
|
backendHandlerPath: handlerPath,
|
|
29
35
|
backendCDKPath: sideEffectBackendPath,
|
|
36
|
+
defaults: BlocksPresets.production,
|
|
30
37
|
});
|
|
31
38
|
const findMarker = (scope) => scope.node.tryFindChild('SideEffectMarker');
|
|
32
39
|
assert.ok(findMarker(backend1), 'Stage1 backend should have SideEffectMarker construct from module side effect');
|
|
@@ -40,6 +47,7 @@ describe('synth shape (drop into existing stack)', () => {
|
|
|
40
47
|
const backend = await BlocksBackend.create(parent, 'Blocks', {
|
|
41
48
|
backendHandlerPath: handlerPath,
|
|
42
49
|
backendCDKPath: sideEffectBackendPath,
|
|
50
|
+
defaults: BlocksPresets.production,
|
|
43
51
|
});
|
|
44
52
|
// Public surface mirrors BlocksStack.
|
|
45
53
|
assert.ok(backend.handler, 'BlocksBackend should expose .handler');
|
|
@@ -57,15 +65,104 @@ describe('synth shape (drop into existing stack)', () => {
|
|
|
57
65
|
await BlocksBackend.create(parent, 'BackendA', {
|
|
58
66
|
backendHandlerPath: handlerPath,
|
|
59
67
|
backendCDKPath: sideEffectBackendPath,
|
|
68
|
+
defaults: BlocksPresets.production,
|
|
60
69
|
});
|
|
61
70
|
await BlocksBackend.create(parent, 'BackendB', {
|
|
62
71
|
backendHandlerPath: handlerPath,
|
|
63
72
|
backendCDKPath: sideEffectBackendPath,
|
|
73
|
+
defaults: BlocksPresets.production,
|
|
64
74
|
});
|
|
65
75
|
const template = Template.fromStack(parent);
|
|
66
76
|
template.resourceCountIs('AWS::ApiGateway::RestApi', 2);
|
|
67
77
|
});
|
|
68
78
|
});
|
|
79
|
+
describe('shared execution role', () => {
|
|
80
|
+
test('exposes executionRole on the backend', async () => {
|
|
81
|
+
const app = new cdk.App();
|
|
82
|
+
const parent = new cdk.Stack(app, 'RoleSurfaceStack');
|
|
83
|
+
const backend = await BlocksBackend.create(parent, 'Blocks', {
|
|
84
|
+
backendHandlerPath: handlerPath,
|
|
85
|
+
backendCDKPath: sideEffectBackendPath,
|
|
86
|
+
defaults: BlocksPresets.production,
|
|
87
|
+
});
|
|
88
|
+
assert.ok(backend.executionRole, 'BlocksBackend should expose .executionRole');
|
|
89
|
+
});
|
|
90
|
+
test('synth produces a Lambda-assumable role with basic execution, and the handler uses it', async () => {
|
|
91
|
+
const app = new cdk.App();
|
|
92
|
+
const parent = new cdk.Stack(app, 'RoleSynthStack');
|
|
93
|
+
await BlocksBackend.create(parent, 'Blocks', {
|
|
94
|
+
backendHandlerPath: handlerPath,
|
|
95
|
+
backendCDKPath: sideEffectBackendPath,
|
|
96
|
+
defaults: BlocksPresets.production,
|
|
97
|
+
});
|
|
98
|
+
const template = Template.fromStack(parent);
|
|
99
|
+
// The shared role (logical id derived from the 'BlocksRole' construct id)
|
|
100
|
+
// is assumable by Lambda and carries AWSLambdaBasicExecutionRole (so
|
|
101
|
+
// CloudWatch Logs keep working after swapping off the auto-role). Other
|
|
102
|
+
// roles exist (API Gateway CloudWatch role, config BucketDeployment role),
|
|
103
|
+
// so we target ours by logical id.
|
|
104
|
+
const roles = template.findResources('AWS::IAM::Role');
|
|
105
|
+
const blocksRoleId = Object.keys(roles).find(k => k.includes('BlocksRole'));
|
|
106
|
+
assert.ok(blocksRoleId, 'expected a role from the BlocksRole construct');
|
|
107
|
+
const blocksRole = roles[blocksRoleId];
|
|
108
|
+
assert.deepStrictEqual(blocksRole.Properties.AssumeRolePolicyDocument.Statement[0], {
|
|
109
|
+
Action: 'sts:AssumeRole',
|
|
110
|
+
Effect: 'Allow',
|
|
111
|
+
Principal: { Service: 'lambda.amazonaws.com' },
|
|
112
|
+
});
|
|
113
|
+
assert.ok(JSON.stringify(blocksRole.Properties.ManagedPolicyArns ?? []).includes('AWSLambdaBasicExecutionRole'), 'BlocksRole should attach AWSLambdaBasicExecutionRole');
|
|
114
|
+
// The Blocks handler references the shared role, not an auto-generated one.
|
|
115
|
+
template.hasResourceProperties('AWS::Lambda::Function', {
|
|
116
|
+
Role: { 'Fn::GetAtt': [blocksRoleId, 'Arn'] },
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
test('a nested block resolves executionRole via the construct-tree walk', async () => {
|
|
120
|
+
const app = new cdk.App();
|
|
121
|
+
const parent = new cdk.Stack(app, 'RoleResolveStack');
|
|
122
|
+
const backend = await BlocksBackend.create(parent, 'Blocks', {
|
|
123
|
+
backendHandlerPath: handlerPath,
|
|
124
|
+
backendCDKPath: sideEffectBackendPath,
|
|
125
|
+
defaults: BlocksPresets.production,
|
|
126
|
+
});
|
|
127
|
+
// Build nested Scopes under the backend (outer → inner), the same shape a
|
|
128
|
+
// real Building Block tree has, and grant a uniquely-named marker action to
|
|
129
|
+
// `this.executionRole` from the innermost scope. If the getter's tree-walk
|
|
130
|
+
// failed, it would resolve the wrong role (or throw), and the marker would
|
|
131
|
+
// not land on the backend's shared role.
|
|
132
|
+
// `create()` sets globalThis.CURRENT_BLOCKS_STACK = backend, so a parent-less
|
|
133
|
+
// Scope attaches under the backend (the same way a real backend module's
|
|
134
|
+
// top-level blocks do); `inner` is then nested one level deeper.
|
|
135
|
+
const outer = new Scope('outer');
|
|
136
|
+
const inner = new Scope('inner', { parent: outer });
|
|
137
|
+
// Resolves to the backend's shared role from two levels deep.
|
|
138
|
+
assert.strictEqual(inner.executionRole, backend.executionRole);
|
|
139
|
+
inner.executionRole.addToPrincipalPolicy(new PolicyStatement({ actions: [EXECUTION_ROLE_MARKER_ACTION], resources: ['*'] }));
|
|
140
|
+
// The grant lands on the shared role's default inline policy (AWS::IAM::Policy).
|
|
141
|
+
const template = Template.fromStack(parent);
|
|
142
|
+
template.hasResourceProperties('AWS::IAM::Policy', {
|
|
143
|
+
PolicyDocument: {
|
|
144
|
+
Statement: Match.arrayWith([Match.objectLike({ Action: EXECUTION_ROLE_MARKER_ACTION })]),
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('CJS bundle: import.meta.url in the handler is shimmed (no Lambda-load crash)', () => {
|
|
150
|
+
test('a handler that uses import.meta.url bundles successfully instead of throwing at load', async () => {
|
|
151
|
+
// The handler is bundled to CJS, where `import.meta` is empty. Left unshimmed,
|
|
152
|
+
// `fileURLToPath(import.meta.url)` compiles to `fileURLToPath(undefined)` and
|
|
153
|
+
// throws at Lambda load (esbuild only warns, so the broken bundle would deploy).
|
|
154
|
+
// blocksNodejsBundling shims import.meta.* to CommonJS equivalents, so bundling
|
|
155
|
+
// (which runs synchronously during construction) succeeds. The runtime behaviour
|
|
156
|
+
// of the emitted shim is verified directly in bundling.test.ts.
|
|
157
|
+
const app = new cdk.App();
|
|
158
|
+
const stack = new cdk.Stack(app, 'ImportMetaStack');
|
|
159
|
+
await assert.doesNotReject(() => BlocksBackend.create(stack, 'blocks', {
|
|
160
|
+
backendHandlerPath: importMetaHandlerPath,
|
|
161
|
+
backendCDKPath: sideEffectBackendPath,
|
|
162
|
+
defaults: BlocksPresets.production,
|
|
163
|
+
}));
|
|
164
|
+
});
|
|
165
|
+
});
|
|
69
166
|
describe('factory function support', () => {
|
|
70
167
|
test('BlocksBackend.create() calls default export function with the backend instance', async () => {
|
|
71
168
|
const app = new cdk.App();
|
|
@@ -73,6 +170,7 @@ describe('factory function support', () => {
|
|
|
73
170
|
const backend = await BlocksBackend.create(stack, 'FactoryStage', {
|
|
74
171
|
backendHandlerPath: handlerPath,
|
|
75
172
|
backendCDKPath: factoryBackendPath,
|
|
173
|
+
defaults: BlocksPresets.production,
|
|
76
174
|
});
|
|
77
175
|
const marker = backend.node.tryFindChild('FactoryMarker');
|
|
78
176
|
assert.ok(marker, 'Factory function should have created FactoryMarker on the backend');
|
|
@@ -85,6 +183,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
|
|
|
85
183
|
const backend = await BlocksBackend.create(stack, 'blocks', {
|
|
86
184
|
backendHandlerPath: handlerPath,
|
|
87
185
|
backendCDKPath: sideEffectBackendPath,
|
|
186
|
+
defaults: BlocksPresets.production,
|
|
88
187
|
});
|
|
89
188
|
assert.strictEqual(backend.fullId, 'TopLevelStack-blocks');
|
|
90
189
|
assert.ok(!cdk.Token.isUnresolved(backend.fullId), 'fullId must not contain a token');
|
|
@@ -102,6 +201,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
|
|
|
102
201
|
const backend = await BlocksBackend.create(nested, 'blocks', {
|
|
103
202
|
backendHandlerPath: handlerPath,
|
|
104
203
|
backendCDKPath: sideEffectBackendPath,
|
|
204
|
+
defaults: BlocksPresets.production,
|
|
105
205
|
});
|
|
106
206
|
assert.ok(!cdk.Token.isUnresolved(backend.fullId), `fullId must be token-free inside a nested stack, got: ${backend.fullId}`);
|
|
107
207
|
// Falls back to the top-level (concrete) stack name, keeping uniqueness.
|
|
@@ -116,6 +216,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
|
|
|
116
216
|
const backend = await BlocksBackend.create(nested, 'blocks', {
|
|
117
217
|
backendHandlerPath: handlerPath,
|
|
118
218
|
backendCDKPath: fullIdConstructBackendPath,
|
|
219
|
+
defaults: BlocksPresets.production,
|
|
119
220
|
});
|
|
120
221
|
// The construct ID is `${scope.fullId}Marker` → `ParentStack-blocks-blocks-dbMarker`.
|
|
121
222
|
const expectedId = `${backend.fullId}-dbMarker`;
|
|
@@ -136,6 +237,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
|
|
|
136
237
|
const backend = await BlocksBackend.create(nested, 'blocks', {
|
|
137
238
|
backendHandlerPath: handlerPath,
|
|
138
239
|
backendCDKPath: sideEffectBackendPath,
|
|
240
|
+
defaults: BlocksPresets.production,
|
|
139
241
|
});
|
|
140
242
|
const template = Template.fromStack(nested);
|
|
141
243
|
const fns = template.findResources('AWS::Lambda::Function');
|
|
@@ -149,3 +251,37 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
|
|
|
149
251
|
}
|
|
150
252
|
});
|
|
151
253
|
});
|
|
254
|
+
describe('infrastructure defaults (backend-anchored)', () => {
|
|
255
|
+
test('each backend exposes its own defaults', async () => {
|
|
256
|
+
const app = new cdk.App();
|
|
257
|
+
const stack = new cdk.Stack(app, 'TwoBackendsStack');
|
|
258
|
+
const a = await BlocksBackend.create(stack, 'A', {
|
|
259
|
+
backendHandlerPath: handlerPath,
|
|
260
|
+
backendCDKPath: sideEffectBackendPath,
|
|
261
|
+
defaults: BlocksPresets.production,
|
|
262
|
+
});
|
|
263
|
+
const b = await BlocksBackend.create(stack, 'B', {
|
|
264
|
+
backendHandlerPath: handlerPath,
|
|
265
|
+
backendCDKPath: sideEffectBackendPath,
|
|
266
|
+
defaults: BlocksPresets.sandbox,
|
|
267
|
+
});
|
|
268
|
+
// Two backends in one stack must NOT clobber each other — defaults are
|
|
269
|
+
// anchored on the backend, not the shared stack.
|
|
270
|
+
assert.strictEqual(a.defaults, BlocksPresets.production);
|
|
271
|
+
assert.strictEqual(b.defaults, BlocksPresets.sandbox);
|
|
272
|
+
});
|
|
273
|
+
test('a nested block resolves its owning backend defaults via the tree-walk', async () => {
|
|
274
|
+
const app = new cdk.App();
|
|
275
|
+
const stack = new cdk.Stack(app, 'ResolveDefaultsStack');
|
|
276
|
+
const backend = await BlocksBackend.create(stack, 'Blocks', {
|
|
277
|
+
backendHandlerPath: handlerPath,
|
|
278
|
+
backendCDKPath: sideEffectBackendPath,
|
|
279
|
+
defaults: BlocksPresets.sandbox,
|
|
280
|
+
});
|
|
281
|
+
// A Scope under the backend resolves scope.defaults by walking up to it.
|
|
282
|
+
const outer = new Scope('outer');
|
|
283
|
+
const inner = new Scope('inner', { parent: outer });
|
|
284
|
+
assert.strictEqual(inner.defaults, backend.defaults);
|
|
285
|
+
assert.strictEqual(inner.defaults, BlocksPresets.sandbox);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { RemovalPolicy } from 'aws-cdk-lib';
|
|
2
|
+
/**
|
|
3
|
+
* Default values for every Amazon-authored Building Block created within a
|
|
4
|
+
* `BlocksStack` or `BlocksBackend`. A per-block option always overrides the
|
|
5
|
+
* corresponding default (`option ?? scope.defaults.field`).
|
|
6
|
+
*
|
|
7
|
+
* Start from {@link BlocksPresets} and override individual fields as needed.
|
|
8
|
+
*/
|
|
9
|
+
export interface BlocksDefaults {
|
|
10
|
+
/**
|
|
11
|
+
* What happens to a stateful resource (table, bucket, user pool) when the
|
|
12
|
+
* stack is deleted, via CDK/CloudFormation: `RETAIN` keeps the resource (and
|
|
13
|
+
* its data) behind; `DESTROY` tears it down. Applies only to CDK/CloudFormation
|
|
14
|
+
* deletions.
|
|
15
|
+
*/
|
|
16
|
+
removalPolicy: RemovalPolicy;
|
|
17
|
+
/**
|
|
18
|
+
* Whether to block deletion of a stateful resource. Unlike `removalPolicy`,
|
|
19
|
+
* this guards against deletion through **any** path — CDK/CloudFormation as
|
|
20
|
+
* well as the CLI, API, and AWS console — until it is turned off.
|
|
21
|
+
*/
|
|
22
|
+
deletionProtection: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Prepared starting points for {@link BlocksDefaults}. Pick one and override
|
|
26
|
+
* individual fields with a spread:
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* defaults: { ...BlocksPresets.production, deletionProtection: false }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare const BlocksPresets: {
|
|
33
|
+
/** Disposable development stacks: tear down cleanly, no delete guard. */
|
|
34
|
+
sandbox: {
|
|
35
|
+
removalPolicy: RemovalPolicy.DESTROY;
|
|
36
|
+
deletionProtection: false;
|
|
37
|
+
};
|
|
38
|
+
/** Durable, protected posture for permanent deployments. */
|
|
39
|
+
production: {
|
|
40
|
+
removalPolicy: RemovalPolicy.RETAIN;
|
|
41
|
+
deletionProtection: true;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=blocks-defaults.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocks-defaults.d.ts","sourceRoot":"","sources":["../../src/cdk/blocks-defaults.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;OAKG;IACH,aAAa,EAAE,aAAa,CAAC;IAE7B;;;;OAIG;IACH,kBAAkB,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;IACzB,yEAAyE;;;;;IAKzE,4DAA4D;;;;;CAKnB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { RemovalPolicy } from 'aws-cdk-lib';
|
|
4
|
+
/**
|
|
5
|
+
* Prepared starting points for {@link BlocksDefaults}. Pick one and override
|
|
6
|
+
* individual fields with a spread:
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* defaults: { ...BlocksPresets.production, deletionProtection: false }
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export const BlocksPresets = {
|
|
13
|
+
/** Disposable development stacks: tear down cleanly, no delete guard. */
|
|
14
|
+
sandbox: {
|
|
15
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
16
|
+
deletionProtection: false,
|
|
17
|
+
},
|
|
18
|
+
/** Durable, protected posture for permanent deployments. */
|
|
19
|
+
production: {
|
|
20
|
+
removalPolicy: RemovalPolicy.RETAIN,
|
|
21
|
+
deletionProtection: true,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocks-defaults.test.d.ts","sourceRoot":"","sources":["../../src/cdk/blocks-defaults.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* BlocksPresets carry the expected posture. Resolution/anchoring (a block
|
|
5
|
+
* reading its owning backend's defaults, and two backends in one stack keeping
|
|
6
|
+
* separate postures) is covered in blocks-backend.test.ts, which has the
|
|
7
|
+
* fixtures to construct real backends.
|
|
8
|
+
*/
|
|
9
|
+
import { test, describe } from 'node:test';
|
|
10
|
+
import assert from 'node:assert';
|
|
11
|
+
import { RemovalPolicy } from 'aws-cdk-lib';
|
|
12
|
+
import { BlocksPresets } from './blocks-defaults.js';
|
|
13
|
+
describe('BlocksPresets', () => {
|
|
14
|
+
test('sandbox is disposable: DESTROY + deletion protection off', () => {
|
|
15
|
+
assert.strictEqual(BlocksPresets.sandbox.removalPolicy, RemovalPolicy.DESTROY);
|
|
16
|
+
assert.strictEqual(BlocksPresets.sandbox.deletionProtection, false);
|
|
17
|
+
});
|
|
18
|
+
test('production is durable: RETAIN + deletion protection on', () => {
|
|
19
|
+
assert.strictEqual(BlocksPresets.production.removalPolicy, RemovalPolicy.RETAIN);
|
|
20
|
+
assert.strictEqual(BlocksPresets.production.deletionProtection, true);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
import { dirname, join } from 'node:path';
|
|
7
7
|
import * as cdk from 'aws-cdk-lib';
|
|
8
8
|
import { BlocksBackend } from './blocks-backend.js';
|
|
9
|
-
import { BlocksStack } from './index.js';
|
|
9
|
+
import { BlocksStack, BlocksPresets, Scope } from './index.js';
|
|
10
10
|
// Simulate the CDK condition being active (tests import CDK files directly)
|
|
11
11
|
before(() => {
|
|
12
12
|
process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS ?? '') + ' --conditions=cdk';
|
|
@@ -21,10 +21,12 @@ describe('ESM cache-busting (multi-stage)', () => {
|
|
|
21
21
|
const stack1 = await BlocksStack.create(app, 'PipelineStage1', {
|
|
22
22
|
backendHandlerPath: handlerPath,
|
|
23
23
|
backendCDKPath: sideEffectBackendPath,
|
|
24
|
+
defaults: BlocksPresets.production,
|
|
24
25
|
});
|
|
25
26
|
const stack2 = await BlocksStack.create(app, 'PipelineStage2', {
|
|
26
27
|
backendHandlerPath: handlerPath,
|
|
27
28
|
backendCDKPath: sideEffectBackendPath,
|
|
29
|
+
defaults: BlocksPresets.production,
|
|
28
30
|
});
|
|
29
31
|
const findMarker = (scope) => scope.node.tryFindChild('SideEffectMarker');
|
|
30
32
|
assert.ok(findMarker(stack1), 'First stack should have SideEffectMarker from module side effect');
|
|
@@ -37,6 +39,7 @@ describe('factory function support', () => {
|
|
|
37
39
|
const stack = await BlocksStack.create(app, 'FactoryBlocksStack', {
|
|
38
40
|
backendHandlerPath: handlerPath,
|
|
39
41
|
backendCDKPath: factoryBackendPath,
|
|
42
|
+
defaults: BlocksPresets.production,
|
|
40
43
|
});
|
|
41
44
|
const marker = stack.node.tryFindChild('FactoryMarker');
|
|
42
45
|
assert.ok(marker, 'Factory function should have created FactoryMarker on the stack');
|
|
@@ -49,11 +52,45 @@ describe('legacy side-effect mode (no default export)', () => {
|
|
|
49
52
|
const backend = await BlocksBackend.create(stack, 'LegacyStage', {
|
|
50
53
|
backendHandlerPath: handlerPath,
|
|
51
54
|
backendCDKPath: sideEffectBackendPath,
|
|
55
|
+
defaults: BlocksPresets.production,
|
|
52
56
|
});
|
|
53
57
|
const marker = backend.node.tryFindChild('SideEffectMarker');
|
|
54
58
|
assert.ok(marker, 'Side-effect-only module should register construct via globalThis.CURRENT_BLOCKS_STACK');
|
|
55
59
|
});
|
|
56
60
|
});
|
|
61
|
+
describe('shared execution role (BlocksStack)', () => {
|
|
62
|
+
// The role synth shape and the Scope.executionRole tree-walk are shared code
|
|
63
|
+
// (setupBlocksInfra + the getter), covered in blocks-backend.test.ts. The only
|
|
64
|
+
// BlocksStack-specific behavior is that its own constructor wires
|
|
65
|
+
// executionRole — a separate code path from BlocksBackend's constructor.
|
|
66
|
+
test('BlocksStack wires executionRole via its constructor', async () => {
|
|
67
|
+
const app = new cdk.App();
|
|
68
|
+
const stack = await BlocksStack.create(app, 'StackRoleStack', {
|
|
69
|
+
backendHandlerPath: handlerPath,
|
|
70
|
+
backendCDKPath: sideEffectBackendPath,
|
|
71
|
+
defaults: BlocksPresets.production,
|
|
72
|
+
});
|
|
73
|
+
assert.ok(stack.executionRole, 'BlocksStack should expose a populated .executionRole');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('executionRole globalThis fallback', () => {
|
|
77
|
+
test('resolves via globalThis.CURRENT_BLOCKS_STACK when no owner is in the tree', async () => {
|
|
78
|
+
const app = new cdk.App();
|
|
79
|
+
const stack = await BlocksStack.create(app, 'FallbackStack', {
|
|
80
|
+
backendHandlerPath: handlerPath,
|
|
81
|
+
backendCDKPath: sideEffectBackendPath,
|
|
82
|
+
defaults: BlocksPresets.production,
|
|
83
|
+
});
|
|
84
|
+
// A Scope whose construct-tree ancestry has no BlocksStack/BlocksBackend
|
|
85
|
+
// (parented under a plain cdk.Stack) exhausts the tree-walk and falls back
|
|
86
|
+
// to globalThis.CURRENT_BLOCKS_STACK. The `as any` is test plumbing — a
|
|
87
|
+
// plain Stack isn't a ScopeParent, but it IS a valid Construct parent.
|
|
88
|
+
const plainStack = new cdk.Stack(app, 'PlainStack');
|
|
89
|
+
globalThis.CURRENT_BLOCKS_STACK = stack;
|
|
90
|
+
const orphan = new Scope('orphan', { parent: plainStack });
|
|
91
|
+
assert.strictEqual(orphan.executionRole, stack.executionRole, 'fallback resolves to the ambient stack role');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
57
94
|
describe('assertCdkConditionActive', () => {
|
|
58
95
|
test('BlocksStack.create() throws when --conditions=cdk is missing', async () => {
|
|
59
96
|
const origNodeOptions = process.env.NODE_OPTIONS;
|
|
@@ -65,6 +102,7 @@ describe('assertCdkConditionActive', () => {
|
|
|
65
102
|
await assert.rejects(BlocksStack.create(app, 'MissingConditionStack', {
|
|
66
103
|
backendHandlerPath: handlerPath,
|
|
67
104
|
backendCDKPath: sideEffectBackendPath,
|
|
105
|
+
defaults: BlocksPresets.production,
|
|
68
106
|
}), (err) => {
|
|
69
107
|
assert.ok(err.message.includes('Missing --conditions=cdk'), `Expected condition error, got: ${err.message}`);
|
|
70
108
|
return true;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type BundlingOptions } from 'aws-cdk-lib/aws-lambda-nodejs';
|
|
2
|
+
/**
|
|
3
|
+
* Wrap a `NodejsFunction` `bundling` config with the framework's hardened esbuild
|
|
4
|
+
* defaults, so every Lambda the framework bundles behaves consistently.
|
|
5
|
+
*
|
|
6
|
+
* **What it fixes.** `NodejsFunction` bundles to **CommonJS**, where `import.meta` is
|
|
7
|
+
* empty. Any bundled code that does `fileURLToPath(import.meta.url)` (a customer
|
|
8
|
+
* handler, a Building Block's `aws-runtime` code, or a dependency) would otherwise
|
|
9
|
+
* become `fileURLToPath(undefined)` and throw at Lambda load — esbuild only *warns*
|
|
10
|
+
* (`empty-import-meta`), so the broken bundle deploys and 502s on first invocation.
|
|
11
|
+
*
|
|
12
|
+
* **How.** For CJS output this shims `import.meta.url` / `import.meta.dirname` /
|
|
13
|
+
* `import.meta.filename` to their CommonJS equivalents (`pathToFileURL(__filename)`,
|
|
14
|
+
* `__dirname`, `__filename`) via an esbuild `--define` + `banner`. This is the same
|
|
15
|
+
* approach esbuild blesses (defining `import.meta` also suppresses the warning) and
|
|
16
|
+
* that Rollup applies by default, so:
|
|
17
|
+
* - a handler that reads `import.meta.url` no longer crashes at load, and
|
|
18
|
+
* - a bundled dependency that merely *contains* `import.meta` (even in dead code) no
|
|
19
|
+
* longer trips a build failure.
|
|
20
|
+
*
|
|
21
|
+
* The value resolves to the **bundled output file** (esbuild flattens the module tree),
|
|
22
|
+
* which is correct for the common cases — a value computed at synth (e.g. a
|
|
23
|
+
* `migrationsPath`) or dead interop fallbacks — but note it does not point at your
|
|
24
|
+
* source layout. Runtime code that must read a file relative to itself should not rely
|
|
25
|
+
* on `import.meta.url` inside a bundle; resolve such paths at synth time or ship the
|
|
26
|
+
* file as an asset. ESM output (`OutputFormat.ESM`) supports `import.meta` natively and
|
|
27
|
+
* is left untouched.
|
|
28
|
+
*
|
|
29
|
+
* All other options (`minify`, `commandHooks`, `externalModules`, other `esbuildArgs`
|
|
30
|
+
* such as `--conditions`, and any caller `banner`) are preserved.
|
|
31
|
+
*
|
|
32
|
+
* @param options - The site-specific `NodejsFunction` bundling options (optional).
|
|
33
|
+
* @returns The same options with the CJS `import.meta` shim merged in.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* new lambda.NodejsFunction(scope, 'Handler', {
|
|
37
|
+
* entry,
|
|
38
|
+
* bundling: blocksNodejsBundling({ minify: true, esbuildArgs: { '--conditions': 'aws-runtime' } }),
|
|
39
|
+
* });
|
|
40
|
+
*/
|
|
41
|
+
export declare function blocksNodejsBundling(options?: BundlingOptions): BundlingOptions;
|
|
42
|
+
//# sourceMappingURL=bundling.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundling.d.ts","sourceRoot":"","sources":["../../src/cdk/bundling.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,+BAA+B,CAAC;AASnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,eAAoB,GAAG,eAAe,CAwBnF"}
|