@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.
Files changed (70) 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 +15 -0
  5. package/dist/cdk/blocks-backend.d.ts.map +1 -1
  6. package/dist/cdk/blocks-backend.js +41 -3
  7. package/dist/cdk/blocks-backend.test.js +137 -1
  8. package/dist/cdk/blocks-defaults.d.ts +44 -0
  9. package/dist/cdk/blocks-defaults.d.ts.map +1 -0
  10. package/dist/cdk/blocks-defaults.js +23 -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 +22 -0
  14. package/dist/cdk/blocks-stack.test.js +39 -1
  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/index.d.ts +52 -0
  25. package/dist/cdk/index.d.ts.map +1 -1
  26. package/dist/cdk/index.js +95 -5
  27. package/dist/cdk/internal.d.ts +24 -0
  28. package/dist/cdk/internal.d.ts.map +1 -0
  29. package/dist/cdk/internal.js +27 -0
  30. package/dist/cdk/mixins.d.ts +18 -4
  31. package/dist/cdk/mixins.d.ts.map +1 -1
  32. package/dist/cdk/mixins.js +31 -7
  33. package/dist/cdk/mixins.test.js +45 -0
  34. package/dist/common/index.d.ts +9 -0
  35. package/dist/common/index.d.ts.map +1 -1
  36. package/dist/index.cdk.d.ts +2 -1
  37. package/dist/index.cdk.d.ts.map +1 -1
  38. package/dist/index.cdk.js +2 -1
  39. package/dist/rpc.d.ts.map +1 -1
  40. package/dist/rpc.js +12 -3
  41. package/dist/rpc.test.js +20 -0
  42. package/dist/scripts/telemetry.test.js +2 -0
  43. package/dist/telemetry/environment.d.ts.map +1 -1
  44. package/dist/telemetry/environment.js +3 -1
  45. package/dist/telemetry/telemetry.test.js +10 -0
  46. package/dist/version.d.ts +1 -1
  47. package/dist/version.d.ts.map +1 -1
  48. package/dist/version.js +1 -1
  49. package/package.json +6 -1
  50. package/src/cdk/__fixtures__/import-meta-handler.ts +16 -0
  51. package/src/cdk/blocks-backend.test.ts +166 -1
  52. package/src/cdk/blocks-backend.ts +55 -3
  53. package/src/cdk/blocks-defaults.test.ts +25 -0
  54. package/src/cdk/blocks-defaults.ts +49 -0
  55. package/src/cdk/blocks-stack.test.ts +48 -1
  56. package/src/cdk/bundling.test.ts +90 -0
  57. package/src/cdk/bundling.ts +76 -0
  58. package/src/cdk/compute/compute.ts +37 -0
  59. package/src/cdk/index.ts +105 -5
  60. package/src/cdk/internal.ts +29 -0
  61. package/src/cdk/mixins.test.ts +56 -1
  62. package/src/cdk/mixins.ts +32 -7
  63. package/src/common/index.ts +9 -0
  64. package/src/index.cdk.ts +5 -1
  65. package/src/rpc.test.ts +22 -0
  66. package/src/rpc.ts +19 -3
  67. package/src/scripts/telemetry.test.ts +2 -0
  68. package/src/telemetry/environment.ts +3 -1
  69. package/src/telemetry/telemetry.test.ts +12 -0
  70. package/src/version.ts +1 -1
@@ -92,6 +92,51 @@ describe('sandbox removal policy', () => {
92
92
  DeletionProtection: Match.absent(),
93
93
  });
94
94
  });
95
+ test('SandboxDisableDeletionProtection mixin: deletionProtection disabled on DynamoDB table', () => {
96
+ const app = new cdk.App({ context: { sandboxMode: 'true' } });
97
+ const stack = new cdk.Stack(app, 'TestStack');
98
+ new Table(stack, 'MyTable', {
99
+ partitionKey: { name: 'pk', type: AttributeType.STRING },
100
+ billingMode: BillingMode.PAY_PER_REQUEST,
101
+ deletionProtection: true,
102
+ });
103
+ RemovalPolicies.of(stack).destroy();
104
+ Mixins.of(stack).apply(new SandboxDisableDeletionProtection());
105
+ const template = Template.fromStack(stack);
106
+ // DynamoDB refuses DeleteTable while protection is on, regardless of DeletionPolicy
107
+ template.hasResourceProperties('AWS::DynamoDB::Table', {
108
+ DeletionProtectionEnabled: false,
109
+ });
110
+ template.hasResource('AWS::DynamoDB::Table', {
111
+ DeletionPolicy: 'Delete',
112
+ });
113
+ });
114
+ test('production mode: deletionProtection stays enabled on DynamoDB table when mixin is not applied', () => {
115
+ const app = new cdk.App();
116
+ const stack = new cdk.Stack(app, 'TestStack');
117
+ new Table(stack, 'MyTable', {
118
+ partitionKey: { name: 'pk', type: AttributeType.STRING },
119
+ billingMode: BillingMode.PAY_PER_REQUEST,
120
+ deletionProtection: true,
121
+ });
122
+ const template = Template.fromStack(stack);
123
+ template.hasResourceProperties('AWS::DynamoDB::Table', {
124
+ DeletionProtectionEnabled: true,
125
+ });
126
+ });
127
+ test('SandboxDisableDeletionProtection mixin: does not set DeletionProtectionEnabled on unprotected tables', () => {
128
+ const app = new cdk.App({ context: { sandboxMode: 'true' } });
129
+ const stack = new cdk.Stack(app, 'TestStack');
130
+ new Table(stack, 'MyTable', {
131
+ partitionKey: { name: 'pk', type: AttributeType.STRING },
132
+ billingMode: BillingMode.PAY_PER_REQUEST,
133
+ });
134
+ Mixins.of(stack).apply(new SandboxDisableDeletionProtection());
135
+ const template = Template.fromStack(stack);
136
+ template.hasResourceProperties('AWS::DynamoDB::Table', {
137
+ DeletionProtectionEnabled: Match.absent(),
138
+ });
139
+ });
95
140
  test('SandboxDisableDeletionProtection mixin: skips constructs without deletionProtection', () => {
96
141
  const app = new cdk.App();
97
142
  const stack = new cdk.Stack(app, 'TestStack');
@@ -1,5 +1,6 @@
1
1
  import type { StackProps } from 'aws-cdk-lib';
2
2
  import type { Construct } from 'constructs';
3
+ import type { BlocksDefaults } from '../cdk/blocks-defaults.js';
3
4
  export { OFFICIAL_BB_NAMES } from './official-bb-names.generated.js';
4
5
  export interface ScopeOptions {
5
6
  parent?: ScopeParent;
@@ -212,6 +213,14 @@ export declare function computeScopeFullId(scope: {
212
213
  export interface BlocksStackProps extends StackProps {
213
214
  backendHandlerPath: string;
214
215
  backendCDKPath: string;
216
+ /**
217
+ * Stack-wide infrastructure defaults applied to every Building Block (removal
218
+ * policy, deletion protection, …). Start from `BlocksPresets.sandbox` or
219
+ * `BlocksPresets.production` and override individual fields as needed. Any
220
+ * field a block also exposes as a per-block option is overridden by that
221
+ * option. See `BlocksDefaults` in `@aws-blocks/core/cdk`.
222
+ */
223
+ defaults: BlocksDefaults;
215
224
  }
216
225
  export declare class BlocksStack {
217
226
  readonly id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsGG;AAEH;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,KAAK;IAChB,SAAgB,EAAE,EAAE,MAAM,CAAC;IAC3B,SAAgB,MAAM,EAAE,WAAW,CAAC;IAEpC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B,kEAAkE;IAClE,OAAO,CAAC,MAAM,CAAC,WAAW,CAA8D;gBAE5E,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;IAiB9C;;;;;;;;OAQG;IACH,MAAM,CAAC,mBAAmB,IAAI;QAAE,MAAM,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE;IAejI,2CAA2C;IAC3C,MAAM,CAAC,cAAc,IAAI,IAAI;IAI7B,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;;;;;;;;OAUG;IACH,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAOlH;;;;OAIG;IACH,wBAAwB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAIxD;;;;;;;;;;OAUG;IACH,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAIrD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,CAAC,mBAAmB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;CAapD;AA0CD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAE,UAUrE;AAED,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAClD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,WAAW;IACvB,SAAgB,EAAE,EAAE,MAAM,CAAC;gBACf,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB;CAGjE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAErE,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsGG;AAEH;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,KAAK;IAChB,SAAgB,EAAE,EAAE,MAAM,CAAC;IAC3B,SAAgB,MAAM,EAAE,WAAW,CAAC;IAEpC,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B,kEAAkE;IAClE,OAAO,CAAC,MAAM,CAAC,WAAW,CAA8D;gBAE5E,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;IAiB9C;;;;;;;;OAQG;IACH,MAAM,CAAC,mBAAmB,IAAI;QAAE,MAAM,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE;IAejI,2CAA2C;IAC3C,MAAM,CAAC,cAAc,IAAI,IAAI;IAI7B,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;;;;;;;;;OAUG;IACH,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAOlH;;;;OAIG;IACH,wBAAwB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAIxD;;;;;;;;;;OAUG;IACH,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAIrD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,CAAC,mBAAmB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE;CAapD;AA0CD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAE,UAUrE;AAED,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAClD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED,qBAAa,WAAW;IACvB,SAAgB,EAAE,EAAE,MAAM,CAAC;gBACf,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB;CAGjE"}
@@ -5,7 +5,8 @@ export { EventSourceMapping } from './lambda-handler.js';
5
5
  export { BlocksStackProps } from './common/index.js';
6
6
  export { registerSdkIdentifiers, getSdkIdentifiers, getAllSdkIdentifiers, _resetSdkRegistry } from './common/sdk-registry.js';
7
7
  export { getConfig, getConfigSync, preloadConfig, loadConfigToProcessEnv, _resetConfigCache } from './common/config.js';
8
- export { BlocksStack, Scope, SandboxDisableDeletionProtection, BlocksBackend, registerConfig, finalizeConfigRegistry, synthGuard, DEFAULT_NODE_RUNTIME, type BlocksBackendProps } from './cdk/index.js';
8
+ export { BlocksStack, Scope, SandboxDisableDeletionProtection, BlocksBackend, registerConfig, finalizeConfigRegistry, synthGuard, DEFAULT_NODE_RUNTIME, blocksNodejsBundling, type BlocksBackendProps } from './cdk/index.js';
9
+ export { type BlocksDefaults, BlocksPresets, } from './cdk/index.js';
9
10
  export { Hosting, type HostingProps, type BlocksStackApi, type FrameworkType, type ComputeConfig, type HostingDomainConfig, type HostingWafConfig, } from './hosting.js';
10
11
  export { RawRoute, RawRouteErrors, type RawRouteOptions, type HttpMethod, } from './raw-route.cdk.js';
11
12
  export { registerRoute, matchRoute, getRegisteredRoutes, clearRouteRegistry, lockRouteRegistry, unlockRouteRegistry, type RegisteredRoute, } from './raw-route.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC9H,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACxH,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,gCAAgC,EAAE,aAAa,EAAE,cAAc,EAAE,sBAAsB,EAAE,UAAU,EAAE,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACxM,OAAO,EACL,OAAO,EACP,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,QAAQ,EACR,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,UAAU,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,aAAa,EACb,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,QAAQ,EACR,WAAW,EACX,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC9H,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACxH,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,gCAAgC,EAAE,aAAa,EAAE,cAAc,EAAE,sBAAsB,EAAE,UAAU,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC9N,OAAO,EACL,KAAK,cAAc,EACnB,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,OAAO,EACP,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,QAAQ,EACR,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,UAAU,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,aAAa,EACb,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,QAAQ,EACR,WAAW,EACX,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC"}
package/dist/index.cdk.js CHANGED
@@ -6,7 +6,8 @@ export { ApiError, isBlocksError, hasAuthError, DEFAULT_API_ERROR_NAME } from '.
6
6
  export { EventSourceMapping } from './lambda-handler.js';
7
7
  export { registerSdkIdentifiers, getSdkIdentifiers, getAllSdkIdentifiers, _resetSdkRegistry } from './common/sdk-registry.js';
8
8
  export { getConfig, getConfigSync, preloadConfig, loadConfigToProcessEnv, _resetConfigCache } from './common/config.js';
9
- export { BlocksStack, Scope, SandboxDisableDeletionProtection, BlocksBackend, registerConfig, finalizeConfigRegistry, synthGuard, DEFAULT_NODE_RUNTIME } from './cdk/index.js';
9
+ export { BlocksStack, Scope, SandboxDisableDeletionProtection, BlocksBackend, registerConfig, finalizeConfigRegistry, synthGuard, DEFAULT_NODE_RUNTIME, blocksNodejsBundling } from './cdk/index.js';
10
+ export { BlocksPresets, } from './cdk/index.js';
10
11
  export { Hosting, } from './hosting.js';
11
12
  export { RawRoute, RawRouteErrors, } from './raw-route.cdk.js';
12
13
  export { registerRoute, matchRoute, getRegisteredRoutes, clearRouteRegistry, lockRouteRegistry, unlockRouteRegistry, } from './raw-route.js';
package/dist/rpc.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"AAgBA,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,yDAAyD;AACzD,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,GACvC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAMpC,qCAAqC;AACrC,eAAO,MAAM,YAAY;;;;;;CAMf,CAAC;AAMX,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAO9F;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAiBxD;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CA0ChE;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAEnF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAOzF;AAED,yCAAyC;AACzC,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAEzF;AAID,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,EAC1B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAMR"}
1
+ {"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"AAgBA,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,yDAAyD;AACzD,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,GACvC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAMpC,qCAAqC;AACrC,eAAO,MAAM,YAAY;;;;;;CAMf,CAAC;AAMX,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAO9F;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAiBxD;AAID;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CA0DhE;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAEnF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAOzF;AAED,yCAAyC;AACzC,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAEzF;AAID,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,EAC1B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAMR"}
package/dist/rpc.js CHANGED
@@ -79,15 +79,24 @@ export function parseRpcRequest(bodyText) {
79
79
  response: errorResponse(RpcErrorCode.InvalidRequest, 'Invalid Request: method must be "namespace.method"', id, { name: 'InvalidRequest' }),
80
80
  };
81
81
  }
82
+ const hasParams = Object.hasOwn(parsed, 'params');
83
+ if (hasParams && (parsed.params === null || typeof parsed.params !== 'object')) {
84
+ return {
85
+ ok: false,
86
+ response: errorResponse(RpcErrorCode.InvalidParams, 'Invalid params: expected an array or object', id, { name: 'InvalidParams' }),
87
+ };
88
+ }
89
+ let args = [];
90
+ if (hasParams) {
91
+ args = Array.isArray(parsed.params) ? parsed.params : Object.values(parsed.params);
92
+ }
82
93
  return {
83
94
  ok: true,
84
95
  request: {
85
96
  apiNamespace: parsed.method.substring(0, dotIndex),
86
97
  method: parsed.method.substring(dotIndex + 1),
87
98
  // JSON-RPC 2.0 §4.2: params may be an array (positional) or object (named).
88
- args: Array.isArray(parsed.params)
89
- ? parsed.params
90
- : Object.values(parsed.params ?? {}),
99
+ args,
91
100
  id,
92
101
  },
93
102
  };
package/dist/rpc.test.js CHANGED
@@ -82,6 +82,26 @@ describe('params decoding', () => {
82
82
  assert.deepStrictEqual(result.request.args, []);
83
83
  });
84
84
  });
85
+ describe('-32602 Invalid Params validation', () => {
86
+ for (const params of ['abc', 42, true, false, null]) {
87
+ it(`rejects ${JSON.stringify(params)} params`, () => {
88
+ const result = parseRpcRequest(JSON.stringify({
89
+ jsonrpc: '2.0',
90
+ method: 'api.echo',
91
+ params,
92
+ id: 'request-1',
93
+ }));
94
+ assert.strictEqual(result.ok, false);
95
+ if (!result.ok) {
96
+ const response = JSON.parse(result.response);
97
+ assert.strictEqual(response.error.code, RpcErrorCode.InvalidParams);
98
+ assert.strictEqual(response.error.data.name, 'InvalidParams');
99
+ assert.ok(response.error.message.includes('expected an array or object'));
100
+ assert.strictEqual(response.id, 'request-1');
101
+ }
102
+ });
103
+ }
104
+ });
85
105
  describe('batch requests (top-level JSON array body)', () => {
86
106
  it('rejects an array body as Invalid Request with a null id', () => {
87
107
  const result = parseRpcRequest(JSON.stringify([
@@ -23,6 +23,8 @@ const NO_CI_ENV = {
23
23
  TF_BUILD: '',
24
24
  BITBUCKET_BUILD_NUMBER: '',
25
25
  BUILDKITE: '',
26
+ RENDER: '',
27
+ TASKCLUSTER_ROOT_URL: '',
26
28
  AWS_BLOCKS_DISABLE_TELEMETRY: '',
27
29
  };
28
30
  function createTmpDir() {
@@ -1 +1 @@
1
- {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/telemetry/environment.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,QAAQ,IAAI,OAAO,GAAG,QAAQ,GAAG,OAAO,CAEvD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;GAEG;AACH,wBAAgB,IAAI,IAAI,OAAO,CAc9B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,SAAS,CAEzD;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,MAAM,GAAG,SAAS,CAiBhD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI;IACpC,EAAE,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,EAAE,EAAE,OAAO,CAAC;IACZ,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAwBA"}
1
+ {"version":3,"file":"environment.d.ts","sourceRoot":"","sources":["../../src/telemetry/environment.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,QAAQ,IAAI,OAAO,GAAG,QAAQ,GAAG,OAAO,CAEvD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;GAEG;AACH,wBAAgB,IAAI,IAAI,OAAO,CAgB9B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,SAAS,CAEzD;AAED;;;GAGG;AACH,wBAAgB,WAAW,IAAI,MAAM,GAAG,SAAS,CAiBhD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI;IACpC,EAAE,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,EAAE,EAAE,OAAO,CAAC;IACZ,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAwBA"}
@@ -25,7 +25,9 @@ export function isCI() {
25
25
  process.env.JENKINS_URL ||
26
26
  process.env.TF_BUILD ||
27
27
  process.env.BITBUCKET_BUILD_NUMBER ||
28
- process.env.BUILDKITE);
28
+ process.env.BUILDKITE ||
29
+ process.env.RENDER ||
30
+ process.env.TASKCLUSTER_ROOT_URL);
29
31
  }
30
32
  /**
31
33
  * Detect the package manager from npm_config_user_agent.
@@ -117,6 +117,8 @@ describe('telemetry/environment', () => {
117
117
  delete process.env.TF_BUILD;
118
118
  delete process.env.BITBUCKET_BUILD_NUMBER;
119
119
  delete process.env.BUILDKITE;
120
+ delete process.env.RENDER;
121
+ delete process.env.TASKCLUSTER_ROOT_URL;
120
122
  });
121
123
  it('returns false when no CI env vars set', () => {
122
124
  assert.strictEqual(isCI(), false);
@@ -133,6 +135,14 @@ describe('telemetry/environment', () => {
133
135
  process.env.CODEBUILD_BUILD_ID = 'build-123';
134
136
  assert.strictEqual(isCI(), true);
135
137
  });
138
+ it('returns true when RENDER is set', () => {
139
+ process.env.RENDER = 'true';
140
+ assert.strictEqual(isCI(), true);
141
+ });
142
+ it('returns true when TASKCLUSTER_ROOT_URL is set', () => {
143
+ process.env.TASKCLUSTER_ROOT_URL = 'https://tc.example.com';
144
+ assert.strictEqual(isCI(), true);
145
+ });
136
146
  });
137
147
  it('detectOS returns a valid platform', () => {
138
148
  const os = detectOS();
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const CORE_VERSION = "0.1.17";
1
+ export declare const CORE_VERSION = "0.2.0";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,YAAY,WAAW,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,YAAY,UAAU,CAAC"}
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by scripts/generate-version.mjs — do not edit manually
2
- export const CORE_VERSION = '0.1.17';
2
+ export const CORE_VERSION = '0.2.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aws-blocks/core",
3
- "version": "0.1.17",
3
+ "version": "0.2.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/aws-devtools-labs/aws-blocks.git",
@@ -32,6 +32,10 @@
32
32
  "types": "./dist/index.cdk.js",
33
33
  "default": "./dist/index.cdk.js"
34
34
  },
35
+ "./cdk/internal": {
36
+ "types": "./dist/cdk/internal.d.ts",
37
+ "default": "./dist/cdk/internal.js"
38
+ },
35
39
  "./client": "./dist/client/index.js",
36
40
  "./lambda-handler": "./dist/lambda-handler.js",
37
41
  "./bb-utils": {
@@ -67,6 +71,7 @@
67
71
  "@types/cross-spawn": "^6.0.6",
68
72
  "@types/http-proxy": "^1.17.16",
69
73
  "@types/node": "^20.0.0",
74
+ "esbuild": "^0.27.0",
70
75
  "typescript": "^5.3.0",
71
76
  "unctx": "^2.0.0"
72
77
  },
@@ -0,0 +1,16 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ // Repro fixture for the "handler bundles to CJS but uses import.meta.url" bug.
5
+ //
6
+ // The handler is bundled to CJS by NodejsFunction. In a CJS bundle `import.meta`
7
+ // is empty, so this top-level `fileURLToPath(import.meta.url)` would become
8
+ // `fileURLToPath(undefined)` and throw at Lambda load. blocksNodejsBundling shims
9
+ // `import.meta.url` to a CommonJS equivalent, so the bundle loads and resolves a
10
+ // real path instead of crashing — see bundling.test.ts.
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ export const moduleDir = fileURLToPath(import.meta.url);
14
+
15
+ // Reference moduleDir so esbuild can't tree-shake the import.meta.url usage away.
16
+ export const handler = async () => ({ statusCode: 200, body: JSON.stringify({ moduleDir }) });
@@ -6,8 +6,11 @@ import assert from 'node:assert';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { dirname, join } from 'node:path';
8
8
  import * as cdk from 'aws-cdk-lib';
9
- import { Template } from 'aws-cdk-lib/assertions';
9
+ import { Template, Match } from 'aws-cdk-lib/assertions';
10
+ import { PolicyStatement } from 'aws-cdk-lib/aws-iam';
10
11
  import { BlocksBackend } from './blocks-backend.js';
12
+ import { BlocksPresets } from './blocks-defaults.js';
13
+ import { Scope } from './index.js';
11
14
 
12
15
  // Simulate the CDK condition being active (tests import CDK files directly)
13
16
  before(() => {
@@ -19,6 +22,8 @@ const handlerPath = join(__dirname, '__fixtures__', 'handler.js');
19
22
  const sideEffectBackendPath = join(__dirname, '__fixtures__', 'side-effect-backend.js');
20
23
  const factoryBackendPath = join(__dirname, '__fixtures__', 'factory-backend.js');
21
24
  const fullIdConstructBackendPath = join(__dirname, '__fixtures__', 'fullid-construct-backend.js');
25
+ const EXECUTION_ROLE_MARKER_ACTION = 'blocks-test:MarkerAction';
26
+ const importMetaHandlerPath = join(__dirname, '__fixtures__', 'import-meta-handler.js');
22
27
 
23
28
  describe('ESM cache-busting (multi-stage)', () => {
24
29
  test('BlocksBackend.create() with same backendCDKPath but different IDs produces constructs in each', async () => {
@@ -28,11 +33,13 @@ describe('ESM cache-busting (multi-stage)', () => {
28
33
  const backend1 = await BlocksBackend.create(stack, 'Stage1', {
29
34
  backendHandlerPath: handlerPath,
30
35
  backendCDKPath: sideEffectBackendPath,
36
+ defaults: BlocksPresets.production,
31
37
  });
32
38
 
33
39
  const backend2 = await BlocksBackend.create(stack, 'Stage2', {
34
40
  backendHandlerPath: handlerPath,
35
41
  backendCDKPath: sideEffectBackendPath,
42
+ defaults: BlocksPresets.production,
36
43
  });
37
44
 
38
45
  const findMarker = (scope: cdk.aws_lambda_nodejs.NodejsFunction | any) =>
@@ -57,6 +64,7 @@ describe('synth shape (drop into existing stack)', () => {
57
64
  const backend = await BlocksBackend.create(parent, 'Blocks', {
58
65
  backendHandlerPath: handlerPath,
59
66
  backendCDKPath: sideEffectBackendPath,
67
+ defaults: BlocksPresets.production,
60
68
  });
61
69
 
62
70
  // Public surface mirrors BlocksStack.
@@ -78,10 +86,12 @@ describe('synth shape (drop into existing stack)', () => {
78
86
  await BlocksBackend.create(parent, 'BackendA', {
79
87
  backendHandlerPath: handlerPath,
80
88
  backendCDKPath: sideEffectBackendPath,
89
+ defaults: BlocksPresets.production,
81
90
  });
82
91
  await BlocksBackend.create(parent, 'BackendB', {
83
92
  backendHandlerPath: handlerPath,
84
93
  backendCDKPath: sideEffectBackendPath,
94
+ defaults: BlocksPresets.production,
85
95
  });
86
96
 
87
97
  const template = Template.fromStack(parent);
@@ -89,6 +99,116 @@ describe('synth shape (drop into existing stack)', () => {
89
99
  });
90
100
  });
91
101
 
102
+ describe('shared execution role', () => {
103
+ test('exposes executionRole on the backend', async () => {
104
+ const app = new cdk.App();
105
+ const parent = new cdk.Stack(app, 'RoleSurfaceStack');
106
+
107
+ const backend = await BlocksBackend.create(parent, 'Blocks', {
108
+ backendHandlerPath: handlerPath,
109
+ backendCDKPath: sideEffectBackendPath,
110
+ defaults: BlocksPresets.production,
111
+ });
112
+
113
+ assert.ok(backend.executionRole, 'BlocksBackend should expose .executionRole');
114
+ });
115
+
116
+ test('synth produces a Lambda-assumable role with basic execution, and the handler uses it', async () => {
117
+ const app = new cdk.App();
118
+ const parent = new cdk.Stack(app, 'RoleSynthStack');
119
+
120
+ await BlocksBackend.create(parent, 'Blocks', {
121
+ backendHandlerPath: handlerPath,
122
+ backendCDKPath: sideEffectBackendPath,
123
+ defaults: BlocksPresets.production,
124
+ });
125
+
126
+ const template = Template.fromStack(parent);
127
+
128
+ // The shared role (logical id derived from the 'BlocksRole' construct id)
129
+ // is assumable by Lambda and carries AWSLambdaBasicExecutionRole (so
130
+ // CloudWatch Logs keep working after swapping off the auto-role). Other
131
+ // roles exist (API Gateway CloudWatch role, config BucketDeployment role),
132
+ // so we target ours by logical id.
133
+ const roles = template.findResources('AWS::IAM::Role');
134
+ const blocksRoleId = Object.keys(roles).find(k => k.includes('BlocksRole'));
135
+ assert.ok(blocksRoleId, 'expected a role from the BlocksRole construct');
136
+ const blocksRole = roles[blocksRoleId];
137
+ assert.deepStrictEqual(blocksRole.Properties.AssumeRolePolicyDocument.Statement[0], {
138
+ Action: 'sts:AssumeRole',
139
+ Effect: 'Allow',
140
+ Principal: { Service: 'lambda.amazonaws.com' },
141
+ });
142
+ assert.ok(
143
+ JSON.stringify(blocksRole.Properties.ManagedPolicyArns ?? []).includes('AWSLambdaBasicExecutionRole'),
144
+ 'BlocksRole should attach AWSLambdaBasicExecutionRole',
145
+ );
146
+
147
+ // The Blocks handler references the shared role, not an auto-generated one.
148
+ template.hasResourceProperties('AWS::Lambda::Function', {
149
+ Role: { 'Fn::GetAtt': [blocksRoleId, 'Arn'] },
150
+ });
151
+ });
152
+
153
+ test('a nested block resolves executionRole via the construct-tree walk', async () => {
154
+ const app = new cdk.App();
155
+ const parent = new cdk.Stack(app, 'RoleResolveStack');
156
+
157
+ const backend = await BlocksBackend.create(parent, 'Blocks', {
158
+ backendHandlerPath: handlerPath,
159
+ backendCDKPath: sideEffectBackendPath,
160
+ defaults: BlocksPresets.production,
161
+ });
162
+
163
+ // Build nested Scopes under the backend (outer → inner), the same shape a
164
+ // real Building Block tree has, and grant a uniquely-named marker action to
165
+ // `this.executionRole` from the innermost scope. If the getter's tree-walk
166
+ // failed, it would resolve the wrong role (or throw), and the marker would
167
+ // not land on the backend's shared role.
168
+ // `create()` sets globalThis.CURRENT_BLOCKS_STACK = backend, so a parent-less
169
+ // Scope attaches under the backend (the same way a real backend module's
170
+ // top-level blocks do); `inner` is then nested one level deeper.
171
+ const outer = new Scope('outer');
172
+ const inner = new Scope('inner', { parent: outer });
173
+
174
+ // Resolves to the backend's shared role from two levels deep.
175
+ assert.strictEqual(inner.executionRole, backend.executionRole);
176
+
177
+ inner.executionRole.addToPrincipalPolicy(
178
+ new PolicyStatement({ actions: [EXECUTION_ROLE_MARKER_ACTION], resources: ['*'] }),
179
+ );
180
+
181
+ // The grant lands on the shared role's default inline policy (AWS::IAM::Policy).
182
+ const template = Template.fromStack(parent);
183
+ template.hasResourceProperties('AWS::IAM::Policy', {
184
+ PolicyDocument: {
185
+ Statement: Match.arrayWith([Match.objectLike({ Action: EXECUTION_ROLE_MARKER_ACTION })]),
186
+ },
187
+ });
188
+ });
189
+ });
190
+
191
+ describe('CJS bundle: import.meta.url in the handler is shimmed (no Lambda-load crash)', () => {
192
+ test('a handler that uses import.meta.url bundles successfully instead of throwing at load', async () => {
193
+ // The handler is bundled to CJS, where `import.meta` is empty. Left unshimmed,
194
+ // `fileURLToPath(import.meta.url)` compiles to `fileURLToPath(undefined)` and
195
+ // throws at Lambda load (esbuild only warns, so the broken bundle would deploy).
196
+ // blocksNodejsBundling shims import.meta.* to CommonJS equivalents, so bundling
197
+ // (which runs synchronously during construction) succeeds. The runtime behaviour
198
+ // of the emitted shim is verified directly in bundling.test.ts.
199
+ const app = new cdk.App();
200
+ const stack = new cdk.Stack(app, 'ImportMetaStack');
201
+
202
+ await assert.doesNotReject(() =>
203
+ BlocksBackend.create(stack, 'blocks', {
204
+ backendHandlerPath: importMetaHandlerPath,
205
+ backendCDKPath: sideEffectBackendPath,
206
+ defaults: BlocksPresets.production,
207
+ }),
208
+ );
209
+ });
210
+ });
211
+
92
212
  describe('factory function support', () => {
93
213
  test('BlocksBackend.create() calls default export function with the backend instance', async () => {
94
214
  const app = new cdk.App();
@@ -97,6 +217,7 @@ describe('factory function support', () => {
97
217
  const backend = await BlocksBackend.create(stack, 'FactoryStage', {
98
218
  backendHandlerPath: handlerPath,
99
219
  backendCDKPath: factoryBackendPath,
220
+ defaults: BlocksPresets.production,
100
221
  });
101
222
 
102
223
  const marker = backend.node.tryFindChild('FactoryMarker');
@@ -112,6 +233,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
112
233
  const backend = await BlocksBackend.create(stack, 'blocks', {
113
234
  backendHandlerPath: handlerPath,
114
235
  backendCDKPath: sideEffectBackendPath,
236
+ defaults: BlocksPresets.production,
115
237
  });
116
238
 
117
239
  assert.strictEqual(backend.fullId, 'TopLevelStack-blocks');
@@ -136,6 +258,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
136
258
  const backend = await BlocksBackend.create(nested, 'blocks', {
137
259
  backendHandlerPath: handlerPath,
138
260
  backendCDKPath: sideEffectBackendPath,
261
+ defaults: BlocksPresets.production,
139
262
  });
140
263
 
141
264
  assert.ok(
@@ -156,6 +279,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
156
279
  const backend = await BlocksBackend.create(nested, 'blocks', {
157
280
  backendHandlerPath: handlerPath,
158
281
  backendCDKPath: fullIdConstructBackendPath,
282
+ defaults: BlocksPresets.production,
159
283
  });
160
284
 
161
285
  // The construct ID is `${scope.fullId}Marker` → `ParentStack-blocks-blocks-dbMarker`.
@@ -183,6 +307,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
183
307
  const backend = await BlocksBackend.create(nested, 'blocks', {
184
308
  backendHandlerPath: handlerPath,
185
309
  backendCDKPath: sideEffectBackendPath,
310
+ defaults: BlocksPresets.production,
186
311
  });
187
312
 
188
313
  const template = Template.fromStack(nested);
@@ -205,3 +330,43 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
205
330
  }
206
331
  });
207
332
  });
333
+
334
+ describe('infrastructure defaults (backend-anchored)', () => {
335
+ test('each backend exposes its own defaults', async () => {
336
+ const app = new cdk.App();
337
+ const stack = new cdk.Stack(app, 'TwoBackendsStack');
338
+
339
+ const a = await BlocksBackend.create(stack, 'A', {
340
+ backendHandlerPath: handlerPath,
341
+ backendCDKPath: sideEffectBackendPath,
342
+ defaults: BlocksPresets.production,
343
+ });
344
+ const b = await BlocksBackend.create(stack, 'B', {
345
+ backendHandlerPath: handlerPath,
346
+ backendCDKPath: sideEffectBackendPath,
347
+ defaults: BlocksPresets.sandbox,
348
+ });
349
+
350
+ // Two backends in one stack must NOT clobber each other — defaults are
351
+ // anchored on the backend, not the shared stack.
352
+ assert.strictEqual(a.defaults, BlocksPresets.production);
353
+ assert.strictEqual(b.defaults, BlocksPresets.sandbox);
354
+ });
355
+
356
+ test('a nested block resolves its owning backend defaults via the tree-walk', async () => {
357
+ const app = new cdk.App();
358
+ const stack = new cdk.Stack(app, 'ResolveDefaultsStack');
359
+
360
+ const backend = await BlocksBackend.create(stack, 'Blocks', {
361
+ backendHandlerPath: handlerPath,
362
+ backendCDKPath: sideEffectBackendPath,
363
+ defaults: BlocksPresets.sandbox,
364
+ });
365
+
366
+ // A Scope under the backend resolves scope.defaults by walking up to it.
367
+ const outer = new Scope('outer');
368
+ const inner = new Scope('inner', { parent: outer });
369
+ assert.strictEqual(inner.defaults, backend.defaults);
370
+ assert.strictEqual(inner.defaults, BlocksPresets.sandbox);
371
+ });
372
+ });