@aws-blocks/core 0.1.18 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/dist/cdk/__fixtures__/import-meta-handler.d.ts +6 -0
  2. package/dist/cdk/__fixtures__/import-meta-handler.d.ts.map +1 -0
  3. package/dist/cdk/__fixtures__/import-meta-handler.js +13 -0
  4. package/dist/cdk/blocks-backend.d.ts +46 -9
  5. package/dist/cdk/blocks-backend.d.ts.map +1 -1
  6. package/dist/cdk/blocks-backend.js +66 -67
  7. package/dist/cdk/blocks-backend.test.js +114 -56
  8. package/dist/cdk/blocks-defaults.d.ts +70 -0
  9. package/dist/cdk/blocks-defaults.d.ts.map +1 -0
  10. package/dist/cdk/blocks-defaults.js +27 -0
  11. package/dist/cdk/blocks-defaults.test.d.ts +2 -0
  12. package/dist/cdk/blocks-defaults.test.d.ts.map +1 -0
  13. package/dist/cdk/blocks-defaults.test.js +24 -0
  14. package/dist/cdk/blocks-stack.test.js +74 -31
  15. package/dist/cdk/bundling.d.ts +42 -0
  16. package/dist/cdk/bundling.d.ts.map +1 -0
  17. package/dist/cdk/bundling.js +72 -0
  18. package/dist/cdk/bundling.test.d.ts +2 -0
  19. package/dist/cdk/bundling.test.d.ts.map +1 -0
  20. package/dist/cdk/bundling.test.js +80 -0
  21. package/dist/cdk/compute/compute.d.ts +33 -0
  22. package/dist/cdk/compute/compute.d.ts.map +1 -0
  23. package/dist/cdk/compute/compute.js +28 -0
  24. package/dist/cdk/compute/default-compute-factory.d.ts +33 -0
  25. package/dist/cdk/compute/default-compute-factory.d.ts.map +1 -0
  26. package/dist/cdk/compute/default-compute-factory.js +3 -0
  27. package/dist/cdk/index.d.ts +92 -12
  28. package/dist/cdk/index.d.ts.map +1 -1
  29. package/dist/cdk/index.js +141 -25
  30. package/dist/cdk/internal.d.ts +25 -0
  31. package/dist/cdk/internal.d.ts.map +1 -0
  32. package/dist/cdk/internal.js +27 -0
  33. package/dist/cdk/mixins.d.ts +18 -4
  34. package/dist/cdk/mixins.d.ts.map +1 -1
  35. package/dist/cdk/mixins.js +31 -7
  36. package/dist/cdk/mixins.test.js +45 -0
  37. package/dist/common/index.d.ts +9 -0
  38. package/dist/common/index.d.ts.map +1 -1
  39. package/dist/hosting-secrets.d.ts +48 -0
  40. package/dist/hosting-secrets.d.ts.map +1 -0
  41. package/dist/hosting-secrets.js +57 -0
  42. package/dist/hosting-secrets.test.d.ts +2 -0
  43. package/dist/hosting-secrets.test.d.ts.map +1 -0
  44. package/dist/hosting-secrets.test.js +81 -0
  45. package/dist/hosting.d.ts +88 -6
  46. package/dist/hosting.d.ts.map +1 -1
  47. package/dist/hosting.js +83 -24
  48. package/dist/hosting.test.js +158 -10
  49. package/dist/index.cdk.d.ts +13 -11
  50. package/dist/index.cdk.d.ts.map +1 -1
  51. package/dist/index.cdk.js +13 -8
  52. package/dist/index.d.ts +8 -7
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +9 -5
  55. package/dist/lambda-handler.test.js +66 -0
  56. package/dist/pipeline/index.d.ts +24 -2
  57. package/dist/pipeline/index.d.ts.map +1 -1
  58. package/dist/pipeline/index.js +41 -1
  59. package/dist/pipeline.test.d.ts +2 -0
  60. package/dist/pipeline.test.d.ts.map +1 -0
  61. package/dist/pipeline.test.js +47 -0
  62. package/dist/rpc.d.ts +21 -0
  63. package/dist/rpc.d.ts.map +1 -1
  64. package/dist/rpc.js +46 -3
  65. package/dist/rpc.test.js +58 -1
  66. package/dist/scripts/config.d.ts +15 -0
  67. package/dist/scripts/config.d.ts.map +1 -0
  68. package/dist/scripts/config.js +29 -0
  69. package/dist/scripts/deploy.d.ts.map +1 -1
  70. package/dist/scripts/deploy.js +4 -0
  71. package/dist/scripts/dev-server.d.ts +8 -0
  72. package/dist/scripts/dev-server.d.ts.map +1 -1
  73. package/dist/scripts/dev-server.js +31 -0
  74. package/dist/scripts/generate-client.d.ts.map +1 -1
  75. package/dist/scripts/generate-client.js +9 -4
  76. package/dist/scripts/generate-client.test.js +19 -4
  77. package/dist/scripts/index.d.ts +13 -10
  78. package/dist/scripts/index.d.ts.map +1 -1
  79. package/dist/scripts/index.js +11 -8
  80. package/dist/scripts/preflight-credentials.d.ts +30 -0
  81. package/dist/scripts/preflight-credentials.d.ts.map +1 -0
  82. package/dist/scripts/preflight-credentials.js +108 -0
  83. package/dist/scripts/preflight-credentials.test.d.ts +2 -0
  84. package/dist/scripts/preflight-credentials.test.d.ts.map +1 -0
  85. package/dist/scripts/preflight-credentials.test.js +77 -0
  86. package/dist/scripts/sandbox-args.test.d.ts +2 -0
  87. package/dist/scripts/sandbox-args.test.d.ts.map +1 -0
  88. package/dist/scripts/sandbox-args.test.js +77 -0
  89. package/dist/scripts/sandbox.d.ts +52 -0
  90. package/dist/scripts/sandbox.d.ts.map +1 -1
  91. package/dist/scripts/sandbox.js +92 -15
  92. package/dist/scripts/secret.d.ts +15 -0
  93. package/dist/scripts/secret.d.ts.map +1 -0
  94. package/dist/scripts/secret.js +29 -0
  95. package/dist/scripts/secret.test.d.ts +2 -0
  96. package/dist/scripts/secret.test.d.ts.map +1 -0
  97. package/dist/scripts/secret.test.js +28 -0
  98. package/dist/scripts/telemetry.test.js +2 -0
  99. package/dist/scripts/typegen.d.ts +15 -0
  100. package/dist/scripts/typegen.d.ts.map +1 -0
  101. package/dist/scripts/typegen.js +16 -0
  102. package/dist/secret-naming.d.ts +58 -0
  103. package/dist/secret-naming.d.ts.map +1 -0
  104. package/dist/secret-naming.js +67 -0
  105. package/dist/secret-naming.test.d.ts +2 -0
  106. package/dist/secret-naming.test.d.ts.map +1 -0
  107. package/dist/secret-naming.test.js +40 -0
  108. package/dist/telemetry/environment.d.ts.map +1 -1
  109. package/dist/telemetry/environment.js +3 -1
  110. package/dist/telemetry/telemetry.test.js +10 -0
  111. package/dist/version.d.ts +1 -1
  112. package/dist/version.d.ts.map +1 -1
  113. package/dist/version.js +1 -1
  114. package/package.json +7 -1
  115. package/src/cdk/__fixtures__/import-meta-handler.ts +16 -0
  116. package/src/cdk/blocks-backend.test.ts +308 -237
  117. package/src/cdk/blocks-backend.ts +98 -77
  118. package/src/cdk/blocks-defaults.test.ts +27 -0
  119. package/src/cdk/blocks-defaults.ts +75 -0
  120. package/src/cdk/blocks-stack.test.ts +160 -110
  121. package/src/cdk/bundling.test.ts +90 -0
  122. package/src/cdk/bundling.ts +76 -0
  123. package/src/cdk/compute/compute.ts +37 -0
  124. package/src/cdk/compute/default-compute-factory.ts +37 -0
  125. package/src/cdk/index.ts +178 -27
  126. package/src/cdk/internal.ts +30 -0
  127. package/src/cdk/mixins.test.ts +56 -1
  128. package/src/cdk/mixins.ts +32 -7
  129. package/src/common/index.ts +9 -0
  130. package/src/hosting-secrets.test.ts +106 -0
  131. package/src/hosting-secrets.ts +106 -0
  132. package/src/hosting.test.ts +244 -63
  133. package/src/hosting.ts +180 -68
  134. package/src/index.cdk.ts +79 -36
  135. package/src/index.ts +35 -16
  136. package/src/lambda-handler.test.ts +74 -0
  137. package/src/pipeline/index.ts +64 -11
  138. package/src/pipeline.test.ts +55 -0
  139. package/src/rpc.test.ts +66 -1
  140. package/src/rpc.ts +60 -3
  141. package/src/scripts/config.ts +35 -0
  142. package/src/scripts/deploy.ts +5 -0
  143. package/src/scripts/dev-server.ts +40 -0
  144. package/src/scripts/generate-client.test.ts +43 -8
  145. package/src/scripts/generate-client.ts +70 -65
  146. package/src/scripts/index.ts +20 -17
  147. package/src/scripts/preflight-credentials.test.ts +110 -0
  148. package/src/scripts/preflight-credentials.ts +131 -0
  149. package/src/scripts/sandbox-args.test.ts +92 -0
  150. package/src/scripts/sandbox.ts +102 -15
  151. package/src/scripts/secret.test.ts +35 -0
  152. package/src/scripts/secret.ts +35 -0
  153. package/src/scripts/telemetry.test.ts +2 -0
  154. package/src/scripts/typegen.ts +18 -0
  155. package/src/secret-naming.test.ts +59 -0
  156. package/src/secret-naming.ts +99 -0
  157. package/src/telemetry/environment.ts +3 -1
  158. package/src/telemetry/telemetry.test.ts +12 -0
  159. package/src/version.ts +1 -1
@@ -0,0 +1,70 @@
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
+ * CORS origins the compute's API accepts, as regular-expression patterns
25
+ * matched against the request `Origin` header. Empty means no cross-origin
26
+ * requests are allowed. The `sandbox` preset allows localhost so a local dev
27
+ * frontend can reach a deployed API; `production` allows none by default.
28
+ */
29
+ allowedOrigins: string[];
30
+ /**
31
+ * Whether stateful resources that support continuous backups keep them on by
32
+ * default — e.g. DynamoDB Point-in-Time Recovery, letting you restore to any
33
+ * second in the retention window. On in `production`, off in `sandbox` (where
34
+ * throwaway data isn't worth the backup-storage cost). Blocks whose service
35
+ * has no equivalent simply ignore it.
36
+ *
37
+ * `true` enables backups with the service's default window; `false` disables
38
+ * them; `{ retentionDays: n }` enables them and pins the window (a block
39
+ * clamps/validates `n` to its service's supported range — DynamoDB PITR is
40
+ * 1–35 days). Backups only have a window when on, so the two are one field.
41
+ */
42
+ pointInTimeRecovery: boolean | {
43
+ retentionDays: number;
44
+ };
45
+ }
46
+ /**
47
+ * Prepared starting points for {@link BlocksDefaults}. Pick one and override
48
+ * individual fields with a spread:
49
+ *
50
+ * ```ts
51
+ * defaults: { ...BlocksPresets.production, deletionProtection: false }
52
+ * ```
53
+ */
54
+ export declare const BlocksPresets: {
55
+ /** Disposable development stacks: tear down cleanly, no delete guard, no backups. */
56
+ sandbox: {
57
+ removalPolicy: RemovalPolicy.DESTROY;
58
+ deletionProtection: false;
59
+ allowedOrigins: string[];
60
+ pointInTimeRecovery: false;
61
+ };
62
+ /** Durable, protected posture for permanent deployments. */
63
+ production: {
64
+ removalPolicy: RemovalPolicy.RETAIN;
65
+ deletionProtection: true;
66
+ allowedOrigins: never[];
67
+ pointInTimeRecovery: true;
68
+ };
69
+ };
70
+ //# 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;IAE5B;;;;;OAKG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IAEzB;;;;;;;;;;;OAWG;IACH,mBAAmB,EAAE,OAAO,GAAG;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC;CACzD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;IACzB,qFAAqF;;;;;;;IAOrF,4DAA4D;;;;;;;CAOnB,CAAC"}
@@ -0,0 +1,27 @@
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, no backups. */
14
+ sandbox: {
15
+ removalPolicy: RemovalPolicy.DESTROY,
16
+ deletionProtection: false,
17
+ allowedOrigins: ['^https?://(localhost|127\\.0\\.0\\.1)(:\\d+)?$'],
18
+ pointInTimeRecovery: false,
19
+ },
20
+ /** Durable, protected posture for permanent deployments. */
21
+ production: {
22
+ removalPolicy: RemovalPolicy.RETAIN,
23
+ deletionProtection: true,
24
+ allowedOrigins: [],
25
+ pointInTimeRecovery: true,
26
+ },
27
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=blocks-defaults.test.d.ts.map
@@ -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,24 @@
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 + no PITR', () => {
15
+ assert.strictEqual(BlocksPresets.sandbox.removalPolicy, RemovalPolicy.DESTROY);
16
+ assert.strictEqual(BlocksPresets.sandbox.deletionProtection, false);
17
+ assert.strictEqual(BlocksPresets.sandbox.pointInTimeRecovery, false);
18
+ });
19
+ test('production is durable: RETAIN + deletion protection on + PITR on', () => {
20
+ assert.strictEqual(BlocksPresets.production.removalPolicy, RemovalPolicy.RETAIN);
21
+ assert.strictEqual(BlocksPresets.production.deletionProtection, true);
22
+ assert.strictEqual(BlocksPresets.production.pointInTimeRecovery, true);
23
+ });
24
+ });
@@ -1,12 +1,49 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { test, describe, before } from 'node:test';
4
3
  import assert from 'node:assert';
5
- import { fileURLToPath } from 'node:url';
6
4
  import { dirname, join } from 'node:path';
5
+ import { before, describe, test } from 'node:test';
6
+ import { fileURLToPath } from 'node:url';
7
7
  import * as cdk from 'aws-cdk-lib';
8
+ import * as apigateway from 'aws-cdk-lib/aws-apigateway';
9
+ import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs';
10
+ import { BLOCKS_RPC_PREFIX } from '../constants.js';
8
11
  import { BlocksBackend } from './blocks-backend.js';
9
- import { BlocksStack, Scope } from './index.js';
12
+ import { Compute } from './compute/compute.js';
13
+ import { BlocksStack, BlocksPresets, Scope } from './index.js';
14
+ // A real app gets its default compute from @aws-blocks/bb-lambda-compute (via
15
+ // @aws-blocks/blocks), which core's own tests can't depend on. Use an
16
+ // equivalent inline stub: a Compute that owns a NodejsFunction + API Gateway,
17
+ // so create() can build the default and the handler/gateway/apiUrl accessors
18
+ // and synth-shape assertions have something real to resolve to. It is passed to
19
+ // each create() via the internal `defaultComputeFactory` option (see makeStack /
20
+ // makeBackend), exactly as @aws-blocks/blocks injects LambdaCompute.
21
+ class StubLambdaCompute extends Compute {
22
+ fn;
23
+ apiGateway;
24
+ apiUrl;
25
+ constructor(scope, id) {
26
+ super(id, { parent: scope });
27
+ this.fn = new lambda.NodejsFunction(this, 'Handler', {
28
+ entry: this.backendHandlerPath,
29
+ runtime: cdk.aws_lambda.Runtime.NODEJS_22_X,
30
+ handler: 'handler',
31
+ role: this.executionRole,
32
+ environment: { BLOCKS_STACK_NAME: this.backendStackName },
33
+ bundling: { minify: true, esbuildArgs: { '--conditions': 'aws-runtime' } },
34
+ });
35
+ this.apiGateway = new apigateway.RestApi(this, 'API', { restApiName: 'Blocks API' });
36
+ this.apiGateway.root.addProxy({
37
+ defaultIntegration: new apigateway.LambdaIntegration(this.fn),
38
+ anyMethod: true,
39
+ });
40
+ this.apiUrl = `${this.apiGateway.url}${BLOCKS_RPC_PREFIX.slice(1)}`;
41
+ }
42
+ setEnv(key, value) {
43
+ this.fn.addEnvironment(key, value);
44
+ }
45
+ }
46
+ const stubComputeFactory = (root) => new StubLambdaCompute(root, 'DefaultCompute');
10
47
  // Simulate the CDK condition being active (tests import CDK files directly)
11
48
  before(() => {
12
49
  process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS ?? '') + ' --conditions=cdk';
@@ -15,17 +52,15 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
15
52
  const handlerPath = join(__dirname, '__fixtures__', 'handler.js');
16
53
  const sideEffectBackendPath = join(__dirname, '__fixtures__', 'side-effect-backend.js');
17
54
  const factoryBackendPath = join(__dirname, '__fixtures__', 'factory-backend.js');
55
+ // Wrap create(), injecting the stub default-compute factory the way
56
+ // @aws-blocks/blocks injects LambdaCompute — so tests don't repeat it.
57
+ const makeStack = (scope, id, backendCDKPath) => BlocksStack.create(scope, id, { backendHandlerPath: handlerPath, backendCDKPath, defaults: BlocksPresets.production, defaultComputeFactory: stubComputeFactory });
58
+ const makeBackend = (scope, id, backendCDKPath) => BlocksBackend.create(scope, id, { backendHandlerPath: handlerPath, backendCDKPath, defaults: BlocksPresets.production, defaultComputeFactory: stubComputeFactory });
18
59
  describe('ESM cache-busting (multi-stage)', () => {
19
60
  test('BlocksStack.create() with same backendCDKPath but different IDs produces constructs in each', async () => {
20
61
  const app = new cdk.App();
21
- const stack1 = await BlocksStack.create(app, 'PipelineStage1', {
22
- backendHandlerPath: handlerPath,
23
- backendCDKPath: sideEffectBackendPath,
24
- });
25
- const stack2 = await BlocksStack.create(app, 'PipelineStage2', {
26
- backendHandlerPath: handlerPath,
27
- backendCDKPath: sideEffectBackendPath,
28
- });
62
+ const stack1 = await makeStack(app, 'PipelineStage1', sideEffectBackendPath);
63
+ const stack2 = await makeStack(app, 'PipelineStage2', sideEffectBackendPath);
29
64
  const findMarker = (scope) => scope.node.tryFindChild('SideEffectMarker');
30
65
  assert.ok(findMarker(stack1), 'First stack should have SideEffectMarker from module side effect');
31
66
  assert.ok(findMarker(stack2), 'Second stack should have SideEffectMarker from re-executed module (cache busted)');
@@ -34,10 +69,7 @@ describe('ESM cache-busting (multi-stage)', () => {
34
69
  describe('factory function support', () => {
35
70
  test('BlocksStack.create() calls default export function with the stack instance', async () => {
36
71
  const app = new cdk.App();
37
- const stack = await BlocksStack.create(app, 'FactoryBlocksStack', {
38
- backendHandlerPath: handlerPath,
39
- backendCDKPath: factoryBackendPath,
40
- });
72
+ const stack = await makeStack(app, 'FactoryBlocksStack', factoryBackendPath);
41
73
  const marker = stack.node.tryFindChild('FactoryMarker');
42
74
  assert.ok(marker, 'Factory function should have created FactoryMarker on the stack');
43
75
  });
@@ -46,10 +78,7 @@ describe('legacy side-effect mode (no default export)', () => {
46
78
  test('module with only side effects still registers constructs via globalThis', async () => {
47
79
  const app = new cdk.App();
48
80
  const stack = new cdk.Stack(app, 'LegacyTestStack');
49
- const backend = await BlocksBackend.create(stack, 'LegacyStage', {
50
- backendHandlerPath: handlerPath,
51
- backendCDKPath: sideEffectBackendPath,
52
- });
81
+ const backend = await makeBackend(stack, 'LegacyStage', sideEffectBackendPath);
53
82
  const marker = backend.node.tryFindChild('SideEffectMarker');
54
83
  assert.ok(marker, 'Side-effect-only module should register construct via globalThis.CURRENT_BLOCKS_STACK');
55
84
  });
@@ -61,20 +90,14 @@ describe('shared execution role (BlocksStack)', () => {
61
90
  // executionRole — a separate code path from BlocksBackend's constructor.
62
91
  test('BlocksStack wires executionRole via its constructor', async () => {
63
92
  const app = new cdk.App();
64
- const stack = await BlocksStack.create(app, 'StackRoleStack', {
65
- backendHandlerPath: handlerPath,
66
- backendCDKPath: sideEffectBackendPath,
67
- });
93
+ const stack = await makeStack(app, 'StackRoleStack', sideEffectBackendPath);
68
94
  assert.ok(stack.executionRole, 'BlocksStack should expose a populated .executionRole');
69
95
  });
70
96
  });
71
97
  describe('executionRole globalThis fallback', () => {
72
98
  test('resolves via globalThis.CURRENT_BLOCKS_STACK when no owner is in the tree', async () => {
73
99
  const app = new cdk.App();
74
- const stack = await BlocksStack.create(app, 'FallbackStack', {
75
- backendHandlerPath: handlerPath,
76
- backendCDKPath: sideEffectBackendPath,
77
- });
100
+ const stack = await makeStack(app, 'FallbackStack', sideEffectBackendPath);
78
101
  // A Scope whose construct-tree ancestry has no BlocksStack/BlocksBackend
79
102
  // (parented under a plain cdk.Stack) exhausts the tree-walk and falls back
80
103
  // to globalThis.CURRENT_BLOCKS_STACK. The `as any` is test plumbing — a
@@ -85,6 +108,29 @@ describe('executionRole globalThis fallback', () => {
85
108
  assert.strictEqual(orphan.executionRole, stack.executionRole, 'fallback resolves to the ambient stack role');
86
109
  });
87
110
  });
111
+ describe('root is bound to the owning stack (multi-stack synth)', () => {
112
+ test('a block under each of two stacks resolves its OWN stack, not the last globalThis', async () => {
113
+ // `root` is resolved once at Scope construction (not re-walked per getter),
114
+ // and `create()` mutates globalThis.CURRENT_BLOCKS_STACK for each stack it
115
+ // builds. With two stacks in one synth, a block constructed under the first
116
+ // must stay bound to the first even after the second stack is created and
117
+ // overwrites globalThis — otherwise its root-derived accessors (handler,
118
+ // executionRole, backendStackName) would silently point at the wrong stack.
119
+ const app = new cdk.App();
120
+ const stackA = await makeStack(app, 'RootBindingA', sideEffectBackendPath);
121
+ // A block explicitly parented under stackA (construct-tree walk resolves
122
+ // stackA regardless of the ambient globalThis).
123
+ const blockA = new Scope('blockA', { parent: stackA });
124
+ // Building the second stack overwrites globalThis.CURRENT_BLOCKS_STACK.
125
+ const stackB = await makeStack(app, 'RootBindingB', sideEffectBackendPath);
126
+ const blockB = new Scope('blockB', { parent: stackB });
127
+ assert.strictEqual(blockA.executionRole, stackA.executionRole, 'blockA stays bound to stackA');
128
+ assert.strictEqual(blockB.executionRole, stackB.executionRole, 'blockB binds to stackB');
129
+ assert.notStrictEqual(stackA.executionRole, stackB.executionRole, 'the two stacks have distinct roles');
130
+ assert.strictEqual(blockA.backendStackName, 'RootBindingA', 'blockA derives its own stack name');
131
+ assert.strictEqual(blockB.backendStackName, 'RootBindingB', 'blockB derives its own stack name');
132
+ });
133
+ });
88
134
  describe('assertCdkConditionActive', () => {
89
135
  test('BlocksStack.create() throws when --conditions=cdk is missing', async () => {
90
136
  const origNodeOptions = process.env.NODE_OPTIONS;
@@ -93,10 +139,7 @@ describe('assertCdkConditionActive', () => {
93
139
  process.execArgv = [];
94
140
  try {
95
141
  const app = new cdk.App();
96
- await assert.rejects(BlocksStack.create(app, 'MissingConditionStack', {
97
- backendHandlerPath: handlerPath,
98
- backendCDKPath: sideEffectBackendPath,
99
- }), (err) => {
142
+ await assert.rejects(makeStack(app, 'MissingConditionStack', sideEffectBackendPath), (err) => {
100
143
  assert.ok(err.message.includes('Missing --conditions=cdk'), `Expected condition error, got: ${err.message}`);
101
144
  return true;
102
145
  });
@@ -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"}
@@ -0,0 +1,72 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs';
4
+ /** Banner-defined identifiers the shim substitutes `import.meta.*` with (CJS only). */
5
+ const IMPORT_META_SHIM = {
6
+ url: '__blocksImportMetaUrl',
7
+ dirname: '__blocksImportMetaDirname',
8
+ filename: '__blocksImportMetaFilename',
9
+ };
10
+ /**
11
+ * Wrap a `NodejsFunction` `bundling` config with the framework's hardened esbuild
12
+ * defaults, so every Lambda the framework bundles behaves consistently.
13
+ *
14
+ * **What it fixes.** `NodejsFunction` bundles to **CommonJS**, where `import.meta` is
15
+ * empty. Any bundled code that does `fileURLToPath(import.meta.url)` (a customer
16
+ * handler, a Building Block's `aws-runtime` code, or a dependency) would otherwise
17
+ * become `fileURLToPath(undefined)` and throw at Lambda load — esbuild only *warns*
18
+ * (`empty-import-meta`), so the broken bundle deploys and 502s on first invocation.
19
+ *
20
+ * **How.** For CJS output this shims `import.meta.url` / `import.meta.dirname` /
21
+ * `import.meta.filename` to their CommonJS equivalents (`pathToFileURL(__filename)`,
22
+ * `__dirname`, `__filename`) via an esbuild `--define` + `banner`. This is the same
23
+ * approach esbuild blesses (defining `import.meta` also suppresses the warning) and
24
+ * that Rollup applies by default, so:
25
+ * - a handler that reads `import.meta.url` no longer crashes at load, and
26
+ * - a bundled dependency that merely *contains* `import.meta` (even in dead code) no
27
+ * longer trips a build failure.
28
+ *
29
+ * The value resolves to the **bundled output file** (esbuild flattens the module tree),
30
+ * which is correct for the common cases — a value computed at synth (e.g. a
31
+ * `migrationsPath`) or dead interop fallbacks — but note it does not point at your
32
+ * source layout. Runtime code that must read a file relative to itself should not rely
33
+ * on `import.meta.url` inside a bundle; resolve such paths at synth time or ship the
34
+ * file as an asset. ESM output (`OutputFormat.ESM`) supports `import.meta` natively and
35
+ * is left untouched.
36
+ *
37
+ * All other options (`minify`, `commandHooks`, `externalModules`, other `esbuildArgs`
38
+ * such as `--conditions`, and any caller `banner`) are preserved.
39
+ *
40
+ * @param options - The site-specific `NodejsFunction` bundling options (optional).
41
+ * @returns The same options with the CJS `import.meta` shim merged in.
42
+ *
43
+ * @example
44
+ * new lambda.NodejsFunction(scope, 'Handler', {
45
+ * entry,
46
+ * bundling: blocksNodejsBundling({ minify: true, esbuildArgs: { '--conditions': 'aws-runtime' } }),
47
+ * });
48
+ */
49
+ export function blocksNodejsBundling(options = {}) {
50
+ // ESM output has real `import.meta` — nothing to shim, and `require` in the banner
51
+ // wouldn't resolve. Only the CommonJS bundle needs the shim.
52
+ if (options.format === OutputFormat.ESM)
53
+ return options;
54
+ const shimBanner = [
55
+ `const ${IMPORT_META_SHIM.url}=require('url').pathToFileURL(__filename).href;`,
56
+ `const ${IMPORT_META_SHIM.dirname}=__dirname;`,
57
+ `const ${IMPORT_META_SHIM.filename}=__filename;`,
58
+ ].join('');
59
+ return {
60
+ ...options,
61
+ // Prepend the shim definitions; keep any caller-supplied banner after them.
62
+ banner: options.banner ? `${shimBanner}\n${options.banner}` : shimBanner,
63
+ esbuildArgs: {
64
+ ...options.esbuildArgs,
65
+ // Substitute import.meta.* with the banner identifiers. Also suppresses esbuild's
66
+ // empty-import-meta warning, so import.meta anywhere in the graph is safe.
67
+ '--define:import.meta.url': IMPORT_META_SHIM.url,
68
+ '--define:import.meta.dirname': IMPORT_META_SHIM.dirname,
69
+ '--define:import.meta.filename': IMPORT_META_SHIM.filename,
70
+ },
71
+ };
72
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bundling.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundling.test.d.ts","sourceRoot":"","sources":["../../src/cdk/bundling.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,80 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { test, describe } from 'node:test';
4
+ import assert from 'node:assert';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { dirname, join } from 'node:path';
7
+ import { mkdtempSync, rmSync } from 'node:fs';
8
+ import { tmpdir } from 'node:os';
9
+ import { createRequire } from 'node:module';
10
+ import { build } from 'esbuild';
11
+ import { OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs';
12
+ import { blocksNodejsBundling } from './bundling.js';
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+ const importMetaFixture = join(__dirname, '__fixtures__', 'import-meta-handler.js');
15
+ const requireCjs = createRequire(import.meta.url);
16
+ describe('blocksNodejsBundling', () => {
17
+ test('injects the import.meta.* CJS shim for the default (CJS) output', () => {
18
+ const out = blocksNodejsBundling({ minify: true, esbuildArgs: { '--conditions': 'aws-runtime' } });
19
+ // Caller options are preserved.
20
+ assert.equal(out.minify, true);
21
+ assert.equal(out.esbuildArgs?.['--conditions'], 'aws-runtime');
22
+ // All three import.meta path properties are substituted.
23
+ assert.ok(out.esbuildArgs?.['--define:import.meta.url']);
24
+ assert.ok(out.esbuildArgs?.['--define:import.meta.dirname']);
25
+ assert.ok(out.esbuildArgs?.['--define:import.meta.filename']);
26
+ // The banner defines the substituted identifiers via CommonJS primitives.
27
+ assert.match(out.banner ?? '', /pathToFileURL\(__filename\)/);
28
+ assert.match(out.banner ?? '', /__dirname/);
29
+ assert.match(out.banner ?? '', /__filename/);
30
+ });
31
+ test('leaves ESM output untouched (import.meta works natively there)', () => {
32
+ const input = { format: OutputFormat.ESM, esbuildArgs: { '--conditions': 'aws-runtime' } };
33
+ const out = blocksNodejsBundling(input);
34
+ assert.deepEqual(out, input);
35
+ assert.equal(out.esbuildArgs?.['--define:import.meta.url'], undefined);
36
+ assert.equal(out.banner, undefined);
37
+ });
38
+ test('prepends the shim while keeping a caller-supplied banner', () => {
39
+ const out = blocksNodejsBundling({ banner: '// caller banner' });
40
+ assert.match(out.banner ?? '', /pathToFileURL\(__filename\)/);
41
+ assert.ok((out.banner ?? '').includes('// caller banner'));
42
+ });
43
+ test('a CJS bundle built with the shim resolves import.meta.url at load (no crash)', async () => {
44
+ // Bundle the fixture exactly as NodejsFunction would: apply the helper's `banner`
45
+ // and its `--define:import.meta.*` esbuildArgs. Without the shim this fixture's
46
+ // top-level `fileURLToPath(import.meta.url)` becomes `fileURLToPath(undefined)`
47
+ // and throws when the module is loaded.
48
+ const opts = blocksNodejsBundling({ minify: true });
49
+ const define = {};
50
+ for (const [key, value] of Object.entries(opts.esbuildArgs ?? {})) {
51
+ const m = key.match(/^--define:(.+)$/);
52
+ if (m)
53
+ define[m[1]] = String(value);
54
+ }
55
+ const tmp = mkdtempSync(join(tmpdir(), 'bb-shim-'));
56
+ const outfile = join(tmp, 'out.cjs');
57
+ try {
58
+ await build({
59
+ entryPoints: [importMetaFixture],
60
+ bundle: true,
61
+ platform: 'node',
62
+ format: 'cjs',
63
+ minify: true,
64
+ banner: { js: opts.banner ?? '' },
65
+ define,
66
+ outfile,
67
+ logLevel: 'silent',
68
+ });
69
+ // Loading the bundle must not throw, and import.meta.url must resolve to a real
70
+ // (file-URL-derived) path rather than being undefined.
71
+ const mod = requireCjs(outfile);
72
+ assert.equal(typeof mod.moduleDir, 'string');
73
+ assert.ok(mod.moduleDir.length > 0, 'moduleDir should resolve to a non-empty path');
74
+ assert.equal(typeof mod.handler, 'function');
75
+ }
76
+ finally {
77
+ rmSync(tmp, { recursive: true, force: true });
78
+ }
79
+ });
80
+ });
@@ -0,0 +1,33 @@
1
+ import { Scope } from '../index.js';
2
+ /**
3
+ * Base class for a Blocks *compute* — a runtime that executes handler code
4
+ * (Lambda today; containers later). A compute owns the physical function/service
5
+ * plus its ingress, and receives config via {@link setEnv}.
6
+ *
7
+ * The backend entry and stack name a compute needs are inherited from
8
+ * {@link Scope} (`backendHandlerPath` / `backendStackName`), which resolve them
9
+ * from the owning BlocksStack/BlocksBackend — never caller-supplied, so every
10
+ * compute in an app runs the same backend and agrees on the resource-name
11
+ * namespace.
12
+ *
13
+ * The abstract base lives in core (a framework primitive); concrete computes
14
+ * live in their own packages (e.g. `LambdaCompute` in `@aws-blocks/bb-lambda-compute`).
15
+ *
16
+ * @internal Not exported from the package's public entry points. Customers
17
+ * cannot instantiate a compute until the customer-facing surface exists.
18
+ */
19
+ export declare abstract class Compute extends Scope {
20
+ /**
21
+ * API namespaces assigned to run on this compute — recorded so request
22
+ * routing can map a namespace to the compute that hosts it. Currently
23
+ * unpopulated (no compute assignment surface yet).
24
+ */
25
+ readonly namespaces: string[];
26
+ /**
27
+ * Inject a runtime configuration value (an environment variable) into this
28
+ * compute. The framework calls this instead of `handler.addEnvironment()`
29
+ * directly so config targets the right compute.
30
+ */
31
+ abstract setEnv(key: string, value: string): void;
32
+ }
33
+ //# sourceMappingURL=compute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compute.d.ts","sourceRoot":"","sources":["../../../src/cdk/compute/compute.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC;;;;;;;;;;;;;;;;GAgBG;AACH,8BAAsB,OAAQ,SAAQ,KAAK;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,CAAM;IAEnC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;CACjD"}
@@ -0,0 +1,28 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { Scope } from '../index.js';
4
+ /**
5
+ * Base class for a Blocks *compute* — a runtime that executes handler code
6
+ * (Lambda today; containers later). A compute owns the physical function/service
7
+ * plus its ingress, and receives config via {@link setEnv}.
8
+ *
9
+ * The backend entry and stack name a compute needs are inherited from
10
+ * {@link Scope} (`backendHandlerPath` / `backendStackName`), which resolve them
11
+ * from the owning BlocksStack/BlocksBackend — never caller-supplied, so every
12
+ * compute in an app runs the same backend and agrees on the resource-name
13
+ * namespace.
14
+ *
15
+ * The abstract base lives in core (a framework primitive); concrete computes
16
+ * live in their own packages (e.g. `LambdaCompute` in `@aws-blocks/bb-lambda-compute`).
17
+ *
18
+ * @internal Not exported from the package's public entry points. Customers
19
+ * cannot instantiate a compute until the customer-facing surface exists.
20
+ */
21
+ export class Compute extends Scope {
22
+ /**
23
+ * API namespaces assigned to run on this compute — recorded so request
24
+ * routing can map a namespace to the compute that hosts it. Currently
25
+ * unpopulated (no compute assignment surface yet).
26
+ */
27
+ namespaces = [];
28
+ }
@@ -0,0 +1,33 @@
1
+ import type * as cdk from 'aws-cdk-lib';
2
+ import type { Compute } from './compute.js';
3
+ import type { BlocksStack } from '../index.js';
4
+ import type { BlocksBackend } from '../blocks-backend.js';
5
+ /**
6
+ * Lambda-shaped surface of the default compute that the legacy
7
+ * `handler` / `gateway` / `apiUrl` accessors on BlocksStack/BlocksBackend read.
8
+ * The default compute (`LambdaCompute` from `@aws-blocks/bb-lambda-compute`)
9
+ * satisfies this structurally, so core exposes those accessors without
10
+ * importing the concrete class. To be removed with those accessors once
11
+ * consumers move to the multi-compute model.
12
+ *
13
+ * @internal
14
+ */
15
+ export interface LambdaShapedCompute extends Compute {
16
+ readonly fn: cdk.aws_lambda_nodejs.NodejsFunction;
17
+ readonly apiGateway: cdk.aws_apigateway.RestApi;
18
+ readonly apiUrl: string;
19
+ }
20
+ /**
21
+ * Builds the default {@link Compute} for a stack/backend. `create()` reads it
22
+ * from its props (`CoreBlocksStackProps.defaultComputeFactory`) and calls it to
23
+ * build the default without importing a concrete compute class — the factory is
24
+ * supplied by whoever owns both core and a concrete compute package (the
25
+ * umbrella `@aws-blocks/blocks`, which injects `LambdaCompute`). It lives on
26
+ * `CoreBlocksStackProps`/`CoreBlocksBackendProps`, which the umbrella derives
27
+ * from the customer-facing props, so it is absent from `BlocksStackProps` and
28
+ * customers cannot set it.
29
+ *
30
+ * @internal
31
+ */
32
+ export type DefaultComputeFactory = (root: BlocksStack | BlocksBackend) => Compute;
33
+ //# sourceMappingURL=default-compute-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-compute-factory.d.ts","sourceRoot":"","sources":["../../../src/cdk/compute/default-compute-factory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,GAAG,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAoB,SAAQ,OAAO;IACnD,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,iBAAiB,CAAC,cAAc,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,WAAW,GAAG,aAAa,KAAK,OAAO,CAAC"}
@@ -0,0 +1,3 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ export {};