@aws-blocks/core 0.2.0 → 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 (123) hide show
  1. package/dist/cdk/blocks-backend.d.ts +35 -9
  2. package/dist/cdk/blocks-backend.d.ts.map +1 -1
  3. package/dist/cdk/blocks-backend.js +52 -69
  4. package/dist/cdk/blocks-backend.test.js +61 -69
  5. package/dist/cdk/blocks-defaults.d.ts +27 -1
  6. package/dist/cdk/blocks-defaults.d.ts.map +1 -1
  7. package/dist/cdk/blocks-defaults.js +5 -1
  8. package/dist/cdk/blocks-defaults.test.js +4 -2
  9. package/dist/cdk/blocks-stack.test.js +73 -37
  10. package/dist/cdk/compute/default-compute-factory.d.ts +33 -0
  11. package/dist/cdk/compute/default-compute-factory.d.ts.map +1 -0
  12. package/dist/cdk/compute/default-compute-factory.js +3 -0
  13. package/dist/cdk/index.d.ts +46 -5
  14. package/dist/cdk/index.d.ts.map +1 -1
  15. package/dist/cdk/index.js +57 -7
  16. package/dist/cdk/internal.d.ts +1 -0
  17. package/dist/cdk/internal.d.ts.map +1 -1
  18. package/dist/hosting-secrets.d.ts +48 -0
  19. package/dist/hosting-secrets.d.ts.map +1 -0
  20. package/dist/hosting-secrets.js +57 -0
  21. package/dist/hosting-secrets.test.d.ts +2 -0
  22. package/dist/hosting-secrets.test.d.ts.map +1 -0
  23. package/dist/hosting-secrets.test.js +81 -0
  24. package/dist/hosting.d.ts +88 -6
  25. package/dist/hosting.d.ts.map +1 -1
  26. package/dist/hosting.js +83 -24
  27. package/dist/hosting.test.js +158 -10
  28. package/dist/index.cdk.d.ts +13 -12
  29. package/dist/index.cdk.d.ts.map +1 -1
  30. package/dist/index.cdk.js +13 -9
  31. package/dist/index.d.ts +8 -7
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +9 -5
  34. package/dist/lambda-handler.test.js +66 -0
  35. package/dist/pipeline/index.d.ts +24 -2
  36. package/dist/pipeline/index.d.ts.map +1 -1
  37. package/dist/pipeline/index.js +41 -1
  38. package/dist/pipeline.test.d.ts +2 -0
  39. package/dist/pipeline.test.d.ts.map +1 -0
  40. package/dist/pipeline.test.js +47 -0
  41. package/dist/rpc.d.ts +21 -0
  42. package/dist/rpc.d.ts.map +1 -1
  43. package/dist/rpc.js +34 -0
  44. package/dist/rpc.test.js +38 -1
  45. package/dist/scripts/config.d.ts +15 -0
  46. package/dist/scripts/config.d.ts.map +1 -0
  47. package/dist/scripts/config.js +29 -0
  48. package/dist/scripts/deploy.d.ts.map +1 -1
  49. package/dist/scripts/deploy.js +4 -0
  50. package/dist/scripts/dev-server.d.ts +8 -0
  51. package/dist/scripts/dev-server.d.ts.map +1 -1
  52. package/dist/scripts/dev-server.js +31 -0
  53. package/dist/scripts/generate-client.d.ts.map +1 -1
  54. package/dist/scripts/generate-client.js +9 -4
  55. package/dist/scripts/generate-client.test.js +19 -4
  56. package/dist/scripts/index.d.ts +13 -10
  57. package/dist/scripts/index.d.ts.map +1 -1
  58. package/dist/scripts/index.js +11 -8
  59. package/dist/scripts/preflight-credentials.d.ts +30 -0
  60. package/dist/scripts/preflight-credentials.d.ts.map +1 -0
  61. package/dist/scripts/preflight-credentials.js +108 -0
  62. package/dist/scripts/preflight-credentials.test.d.ts +2 -0
  63. package/dist/scripts/preflight-credentials.test.d.ts.map +1 -0
  64. package/dist/scripts/preflight-credentials.test.js +77 -0
  65. package/dist/scripts/sandbox-args.test.d.ts +2 -0
  66. package/dist/scripts/sandbox-args.test.d.ts.map +1 -0
  67. package/dist/scripts/sandbox-args.test.js +77 -0
  68. package/dist/scripts/sandbox.d.ts +52 -0
  69. package/dist/scripts/sandbox.d.ts.map +1 -1
  70. package/dist/scripts/sandbox.js +92 -15
  71. package/dist/scripts/secret.d.ts +15 -0
  72. package/dist/scripts/secret.d.ts.map +1 -0
  73. package/dist/scripts/secret.js +29 -0
  74. package/dist/scripts/secret.test.d.ts +2 -0
  75. package/dist/scripts/secret.test.d.ts.map +1 -0
  76. package/dist/scripts/secret.test.js +28 -0
  77. package/dist/scripts/typegen.d.ts +15 -0
  78. package/dist/scripts/typegen.d.ts.map +1 -0
  79. package/dist/scripts/typegen.js +16 -0
  80. package/dist/secret-naming.d.ts +58 -0
  81. package/dist/secret-naming.d.ts.map +1 -0
  82. package/dist/secret-naming.js +67 -0
  83. package/dist/secret-naming.test.d.ts +2 -0
  84. package/dist/secret-naming.test.d.ts.map +1 -0
  85. package/dist/secret-naming.test.js +40 -0
  86. package/dist/version.d.ts +1 -1
  87. package/dist/version.js +1 -1
  88. package/package.json +2 -1
  89. package/src/cdk/blocks-backend.test.ts +267 -272
  90. package/src/cdk/blocks-backend.ts +71 -79
  91. package/src/cdk/blocks-defaults.test.ts +4 -2
  92. package/src/cdk/blocks-defaults.ts +27 -1
  93. package/src/cdk/blocks-stack.test.ts +161 -118
  94. package/src/cdk/compute/default-compute-factory.ts +37 -0
  95. package/src/cdk/index.ts +84 -8
  96. package/src/cdk/internal.ts +1 -0
  97. package/src/hosting-secrets.test.ts +106 -0
  98. package/src/hosting-secrets.ts +106 -0
  99. package/src/hosting.test.ts +244 -63
  100. package/src/hosting.ts +180 -68
  101. package/src/index.cdk.ts +77 -38
  102. package/src/index.ts +35 -16
  103. package/src/lambda-handler.test.ts +74 -0
  104. package/src/pipeline/index.ts +64 -11
  105. package/src/pipeline.test.ts +55 -0
  106. package/src/rpc.test.ts +44 -1
  107. package/src/rpc.ts +41 -0
  108. package/src/scripts/config.ts +35 -0
  109. package/src/scripts/deploy.ts +5 -0
  110. package/src/scripts/dev-server.ts +40 -0
  111. package/src/scripts/generate-client.test.ts +43 -8
  112. package/src/scripts/generate-client.ts +70 -65
  113. package/src/scripts/index.ts +20 -17
  114. package/src/scripts/preflight-credentials.test.ts +110 -0
  115. package/src/scripts/preflight-credentials.ts +131 -0
  116. package/src/scripts/sandbox-args.test.ts +92 -0
  117. package/src/scripts/sandbox.ts +102 -15
  118. package/src/scripts/secret.test.ts +35 -0
  119. package/src/scripts/secret.ts +35 -0
  120. package/src/scripts/typegen.ts +18 -0
  121. package/src/secret-naming.test.ts +59 -0
  122. package/src/secret-naming.ts +99 -0
  123. package/src/version.ts +1 -1
@@ -1,8 +1,10 @@
1
1
  import * as cdk from 'aws-cdk-lib';
2
- import * as apigateway from 'aws-cdk-lib/aws-apigateway';
2
+ import type * as apigateway from 'aws-cdk-lib/aws-apigateway';
3
3
  import * as iam from 'aws-cdk-lib/aws-iam';
4
4
  import { Construct } from 'constructs';
5
5
  import type { BlocksDefaults } from './blocks-defaults.js';
6
+ import type { Compute } from './compute/compute.js';
7
+ import type { DefaultComputeFactory } from './compute/default-compute-factory.js';
6
8
  /**
7
9
  * Validate that the Node.js process was started with `--conditions=cdk`.
8
10
  *
@@ -12,6 +14,12 @@ import type { BlocksDefaults } from './blocks-defaults.js';
12
14
  * no real infrastructure (no tables, no IAM, no Lambda configs).
13
15
  */
14
16
  export declare function assertCdkConditionActive(): void;
17
+ /**
18
+ * Timeout of the shared Blocks handler Lambda, and therefore the ceiling on a
19
+ * single invocation. Exported because resources that feed the handler have to
20
+ * size their own timeouts against it (e.g. an SQS queue's visibility timeout).
21
+ */
22
+ export declare const SHARED_HANDLER_TIMEOUT_SECONDS: number;
15
23
  export interface BlocksBackendProps {
16
24
  backendHandlerPath: string;
17
25
  backendCDKPath: string;
@@ -24,11 +32,23 @@ export interface BlocksBackendProps {
24
32
  */
25
33
  defaults: BlocksDefaults;
26
34
  }
27
- /** Shared infra setup — creates Lambda + API Gateway on the given scope. */
35
+ /**
36
+ * Core's `create()` props: the public {@link BlocksBackendProps} plus the
37
+ * required `defaultComputeFactory`, spread on by the umbrella (`@aws-blocks/blocks`).
38
+ * Customers use {@link BlocksBackendProps} and never set the factory.
39
+ *
40
+ * @internal
41
+ */
42
+ export interface CoreBlocksBackendProps extends BlocksBackendProps {
43
+ /** Builds the backend's default compute. Injected by `@aws-blocks/blocks`. */
44
+ defaultComputeFactory: DefaultComputeFactory;
45
+ }
46
+ /**
47
+ * Shared infra setup — provisions the stack-level resources that are NOT owned
48
+ * by a compute: the shared execution role, resource groups, and console-redirect
49
+ * routes.
50
+ */
28
51
  export declare function setupBlocksInfra(scope: Construct, props: BlocksBackendProps, id?: string): {
29
- handler: cdk.aws_lambda_nodejs.NodejsFunction;
30
- gateway: cdk.aws_apigateway.RestApi;
31
- apiUrl: string;
32
52
  executionRole: cdk.aws_iam.Role;
33
53
  };
34
54
  /**
@@ -49,14 +69,20 @@ export declare function setupBlocksInfra(scope: Construct, props: BlocksBackendP
49
69
  * ```
50
70
  */
51
71
  export declare class BlocksBackend extends Construct {
52
- readonly apiUrl: string;
53
- readonly gateway: apigateway.RestApi;
54
- readonly handler: cdk.aws_lambda_nodejs.NodejsFunction;
55
72
  readonly backendHandlerPath: string;
56
73
  /** Shared IAM role assumed by all Blocks compute. Building Blocks grant to this role. */
57
74
  readonly executionRole: iam.IRole;
58
75
  /** Infrastructure defaults for Building Blocks created under this backend. */
59
76
  readonly defaults: BlocksDefaults;
77
+ /** The default compute (owns the Lambda function + API Gateway); set in `create()`. @internal */
78
+ _defaultCompute?: Compute;
79
+ /** The default compute's Lambda function. To be removed once consumers move to the multi-compute model. */
80
+ get handler(): cdk.aws_lambda_nodejs.NodejsFunction;
81
+ /** The default compute's API Gateway REST API. To be removed once consumers move to the multi-compute model. */
82
+ get gateway(): apigateway.RestApi;
83
+ /** The default compute's RPC endpoint URL. To be removed once consumers move to the multi-compute model. */
84
+ get apiUrl(): string;
85
+ private requireDefaultCompute;
60
86
  /**
61
87
  * The fullId used by child Scopes to compute their env var names,
62
88
  * construct IDs, and physical resource names (e.g., DynamoDB table names).
@@ -78,6 +104,6 @@ export declare class BlocksBackend extends Construct {
78
104
  */
79
105
  get fullId(): string;
80
106
  private constructor();
81
- static create(scope: Construct, id: string, props: BlocksBackendProps): Promise<BlocksBackend>;
107
+ static create(scope: Construct, id: string, props: CoreBlocksBackendProps): Promise<BlocksBackend>;
82
108
  }
83
109
  //# sourceMappingURL=blocks-backend.d.ts.map
@@ -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;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"}
1
+ {"version":3,"file":"blocks-backend.d.ts","sourceRoot":"","sources":["../../src/cdk/blocks-backend.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,GAAG,MAAM,aAAa,CAAC;AACnC,OAAO,KAAK,KAAK,UAAU,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAE,qBAAqB,EAAuB,MAAM,sCAAsC,CAAC;AAEvG;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAkB/C;AAED;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,QAAU,CAAC;AAEtD,MAAM,WAAW,kBAAkB;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAChE,8EAA8E;IAC9E,qBAAqB,EAAE,qBAAqB,CAAC;CAC9C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,CAAC,EAAE,MAAM;;EAwFxF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,aAAc,SAAQ,SAAS;IAC1C,SAAgB,kBAAkB,EAAE,MAAM,CAAC;IAC3C,yFAAyF;IACzF,SAAgB,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC;IACzC,8EAA8E;IAC9E,SAAgB,QAAQ,EAAE,cAAc,CAAC;IACzC,iGAAiG;IACjG,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,2GAA2G;IAC3G,IAAI,OAAO,IAAI,GAAG,CAAC,iBAAiB,CAAC,cAAc,CAElD;IACD,gHAAgH;IAChH,IAAI,OAAO,IAAI,UAAU,CAAC,OAAO,CAEhC;IACD,4GAA4G;IAC5G,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,OAAO,CAAC,qBAAqB;IAO7B;;;;;;;;;;;;;;;;;;OAkBG;IACH,IAAI,MAAM,IAAI,MAAM,CAUnB;IAED,OAAO;WAmBM,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB;CA6BhF"}
@@ -1,17 +1,12 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import * as cdk from 'aws-cdk-lib';
4
- import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs';
5
- import * as apigateway from 'aws-cdk-lib/aws-apigateway';
6
4
  import * as iam from 'aws-cdk-lib/aws-iam';
7
5
  import { CfnGroup } from 'aws-cdk-lib/aws-resourcegroups';
8
6
  import { Construct } from 'constructs';
9
7
  import { pathToFileURL } from 'node:url';
10
- import { DEFAULT_NODE_RUNTIME } from './node-version.js';
11
- import { blocksNodejsBundling } from './bundling.js';
12
8
  import { addBlocksStackMetadata } from './stack-metadata.js';
13
9
  import { finalizeConfigRegistry, registerConfig } from './config-registry.js';
14
- import { BLOCKS_NAMESPACE, BLOCKS_RPC_PREFIX } from '../constants.js';
15
10
  import { registerBuiltinRoutes } from '../builtin-routes.js';
16
11
  /**
17
12
  * Validate that the Node.js process was started with `--conditions=cdk`.
@@ -35,7 +30,17 @@ export function assertCdkConditionActive() {
35
30
  'Or use the Blocks CLI commands (npm run deploy / npm run sandbox) which set this automatically.');
36
31
  }
37
32
  }
38
- /** Shared infra setup — creates Lambda + API Gateway on the given scope. */
33
+ /**
34
+ * Timeout of the shared Blocks handler Lambda, and therefore the ceiling on a
35
+ * single invocation. Exported because resources that feed the handler have to
36
+ * size their own timeouts against it (e.g. an SQS queue's visibility timeout).
37
+ */
38
+ export const SHARED_HANDLER_TIMEOUT_SECONDS = 60 * 15;
39
+ /**
40
+ * Shared infra setup — provisions the stack-level resources that are NOT owned
41
+ * by a compute: the shared execution role, resource groups, and console-redirect
42
+ * routes.
43
+ */
39
44
  export function setupBlocksInfra(scope, props, id) {
40
45
  // Fail fast with an actionable message at the create() call site if `defaults`
41
46
  // is missing (e.g. a plain-JS caller, `as any`, or a dynamically-built props
@@ -45,12 +50,19 @@ export function setupBlocksInfra(scope, props, id) {
45
50
  throw new Error('BlocksStack/BlocksBackend requires a `defaults` field. Pass a posture from ' +
46
51
  '`@aws-blocks/core/cdk` — typically `defaults: sandboxMode ? BlocksPresets.sandbox : BlocksPresets.production`.');
47
52
  }
48
- // ── Shared execution role ──────────────────────────────────────────────
53
+ // ── Shared execution role ───────────────────────────────────────────────
49
54
  // A single IAM role that every Building Block grants to. Provisioned here so
50
55
  // it exists before the backend module is imported (Building Blocks reach it
51
56
  // via `scope.executionRole`). Block grants sit on the role's default (inline)
52
- // policy. AWSLambdaBasicExecutionRole is attached so the handler retains
57
+ // policy. AWSLambdaBasicExecutionRole is attached so compute functions retain
53
58
  // CloudWatch Logs permissions.
59
+ //
60
+ // INVARIANT: this must be a mutable, framework-owned `iam.Role` — never an
61
+ // imported role (`Role.fromRoleArn`/`fromRoleName`), which is immutable by
62
+ // default. On an immutable role, every Building Block's `grant*()` /
63
+ // `addToPrincipalPolicy()` silently becomes a no-op (returns false, no error),
64
+ // so permissions would quietly vanish. If a bring-your-own-role option is ever
65
+ // added, it must resolve to a mutable role (`{ mutable: true }`).
54
66
  const executionRole = new iam.Role(scope, 'BlocksRole', {
55
67
  // CompositePrincipal (rather than a bare ServicePrincipal) so additional
56
68
  // compute types can assume this same shared role as they are introduced
@@ -60,55 +72,7 @@ export function setupBlocksInfra(scope, props, id) {
60
72
  iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
61
73
  ],
62
74
  });
63
- const handler = new lambda.NodejsFunction(scope, 'Handler', {
64
- entry: props.backendHandlerPath,
65
- runtime: DEFAULT_NODE_RUNTIME,
66
- handler: 'handler',
67
- role: executionRole,
68
- memorySize: 2048,
69
- timeout: cdk.Duration.seconds(60 * 15),
70
- environment: {
71
- NODE_ENV: 'production',
72
- /**
73
- * BLOCKS_STACK_NAME is used at runtime to derive physical resource names
74
- * (DynamoDB table names, env var prefixes). It must match the CDK-time
75
- * fullId of the BlocksStack/BlocksBackend so resource lookups work correctly.
76
- *
77
- * For BlocksStack: this equals the stack name (id).
78
- * For BlocksBackend: the caller overrides this after construction to include
79
- * the parent stack name for deployment uniqueness.
80
- */
81
- BLOCKS_STACK_NAME: id ?? cdk.Stack.of(scope).stackName,
82
- },
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({
87
- minify: true,
88
- esbuildArgs: { '--conditions': 'aws-runtime' },
89
- }),
90
- });
91
- // In sandbox mode, allow localhost origins so the local dev frontend can
92
- // reach the deployed Lambda API via CORS.
93
- const isSandbox = scope.node.tryGetContext('sandboxMode') === 'true' ||
94
- scope.node.tryGetContext('sandboxMode') === true;
95
- if (isSandbox) {
96
- handler.addEnvironment('CORS_ALLOWED_ORIGINS', '^https?://(localhost|127\\.0\\.0\\.1)(:\\d+)?$');
97
- }
98
- const api = new apigateway.RestApi(scope, 'API', {
99
- restApiName: 'Blocks API',
100
- deployOptions: { cachingEnabled: false },
101
- });
102
- const integration = new apigateway.LambdaIntegration(handler);
103
- // Build the nested resource tree for /aws-blocks/api.
104
- // Intermediate resource gets a proxy so sub-paths (RawRoutes) still reach Lambda.
105
- const awsBlocksResource = api.root.addResource(BLOCKS_NAMESPACE.slice(1));
106
- awsBlocksResource.addProxy({ defaultIntegration: integration, anyMethod: true });
107
- const apiResource = awsBlocksResource.addResource('api');
108
- apiResource.addMethod('POST', integration);
109
- apiResource.addMethod('OPTIONS', integration);
110
- api.root.addProxy({ defaultIntegration: integration, anyMethod: true });
111
- // ── Resource Groups ────────────────────────────────────────────────────
75
+ // ── Resource Groups ───────────────────────────────────────────────────
112
76
  let rootStack = cdk.Stack.of(scope);
113
77
  while (rootStack.nestedStackParent)
114
78
  rootStack = rootStack.nestedStackParent;
@@ -142,7 +106,7 @@ export function setupBlocksInfra(scope, props, id) {
142
106
  },
143
107
  },
144
108
  });
145
- // ── Console redirect routes ────────────────────────────────────────────
109
+ // ── Console redirect routes ───────────────────────────────────────────
146
110
  const region = cdk.Fn.ref('AWS::Region');
147
111
  const resourcesUrl = cdk.Fn.join('', [
148
112
  'https://', region, '.console.aws.amazon.com/resource-groups/group/',
@@ -155,7 +119,7 @@ export function setupBlocksInfra(scope, props, id) {
155
119
  registerConfig(scope, 'BB_RESOURCES_GROUP_URL', resourcesUrl);
156
120
  registerConfig(scope, 'BB_SETTINGS_GROUP_URL', settingsUrl);
157
121
  registerBuiltinRoutes();
158
- return { handler, gateway: api, apiUrl: `${api.url}${BLOCKS_RPC_PREFIX.slice(1)}`, executionRole };
122
+ return { executionRole };
159
123
  }
160
124
  /**
161
125
  * Standalone CDK construct that provisions the Blocks backend: a single Lambda
@@ -175,14 +139,31 @@ export function setupBlocksInfra(scope, props, id) {
175
139
  * ```
176
140
  */
177
141
  export class BlocksBackend extends Construct {
178
- apiUrl;
179
- gateway;
180
- handler;
181
142
  backendHandlerPath;
182
143
  /** Shared IAM role assumed by all Blocks compute. Building Blocks grant to this role. */
183
144
  executionRole;
184
145
  /** Infrastructure defaults for Building Blocks created under this backend. */
185
146
  defaults;
147
+ /** The default compute (owns the Lambda function + API Gateway); set in `create()`. @internal */
148
+ _defaultCompute;
149
+ /** The default compute's Lambda function. To be removed once consumers move to the multi-compute model. */
150
+ get handler() {
151
+ return this.requireDefaultCompute().fn;
152
+ }
153
+ /** The default compute's API Gateway REST API. To be removed once consumers move to the multi-compute model. */
154
+ get gateway() {
155
+ return this.requireDefaultCompute().apiGateway;
156
+ }
157
+ /** The default compute's RPC endpoint URL. To be removed once consumers move to the multi-compute model. */
158
+ get apiUrl() {
159
+ return this.requireDefaultCompute().apiUrl;
160
+ }
161
+ requireDefaultCompute() {
162
+ if (!this._defaultCompute) {
163
+ throw new Error('Blocks backend not fully initialized — access .handler/.gateway/.apiUrl after BlocksBackend.create() resolves.');
164
+ }
165
+ return this._defaultCompute;
166
+ }
186
167
  /**
187
168
  * The fullId used by child Scopes to compute their env var names,
188
169
  * construct IDs, and physical resource names (e.g., DynamoDB table names).
@@ -223,18 +204,20 @@ export class BlocksBackend extends Construct {
223
204
  // walking up to their owning backend (see Scope.defaults).
224
205
  this.defaults = props.defaults;
225
206
  const infra = setupBlocksInfra(this, props, id);
226
- this.handler = infra.handler;
227
- this.gateway = infra.gateway;
228
- this.apiUrl = infra.apiUrl;
229
207
  this.executionRole = infra.executionRole;
230
- // Override BLOCKS_STACK_NAME to include the parent stack name so runtime
231
- // resource lookups (DynamoDB table names) match the CDK-time fullId
232
- // and are unique per deployment.
233
- this.handler.addEnvironment('BLOCKS_STACK_NAME', this.fullId);
208
+ // The default compute (and thus handler/gateway) is created in create(),
209
+ // after construction it derives BLOCKS_STACK_NAME from this.fullId.
234
210
  }
235
211
  static async create(scope, id, props) {
236
212
  assertCdkConditionActive();
237
213
  const backend = new BlocksBackend(scope, id, props);
214
+ // Create the default compute before importing the backend: it OWNS the
215
+ // Lambda function + API Gateway (which back .handler/.gateway/.apiUrl), and
216
+ // a block reading `this.compute` in its constructor (during that import)
217
+ // must resolve to it. The factory is supplied by the umbrella
218
+ // @aws-blocks/blocks (which injects LambdaCompute) via props, so core never
219
+ // imports the concrete compute class.
220
+ backend._defaultCompute = props.defaultComputeFactory(backend);
238
221
  // file:// URL (not a raw path) so the cache-busting query works on Windows,
239
222
  // where an absolute path like `D:\...` is rejected as URL scheme `d:`.
240
223
  const backendUrl = pathToFileURL(props.backendCDKPath);
@@ -1,15 +1,52 @@
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 { Template, Match } from 'aws-cdk-lib/assertions';
8
+ import { Match, Template } from 'aws-cdk-lib/assertions';
9
+ import * as apigateway from 'aws-cdk-lib/aws-apigateway';
9
10
  import { PolicyStatement } from 'aws-cdk-lib/aws-iam';
11
+ import * as lambda from 'aws-cdk-lib/aws-lambda-nodejs';
12
+ import { BLOCKS_RPC_PREFIX } from '../constants.js';
10
13
  import { BlocksBackend } from './blocks-backend.js';
11
14
  import { BlocksPresets } from './blocks-defaults.js';
15
+ import { Compute } from './compute/compute.js';
12
16
  import { Scope } from './index.js';
17
+ // A real app gets its default compute from @aws-blocks/bb-lambda-compute (via
18
+ // @aws-blocks/blocks), which core's own tests can't depend on. Use an
19
+ // equivalent inline stub: a Compute that owns a NodejsFunction + API Gateway,
20
+ // so create() can build the default and the handler/gateway/apiUrl accessors
21
+ // and synth-shape assertions have something real to resolve to. It is passed to
22
+ // each create() via the internal `defaultComputeFactory` option (see
23
+ // makeBackend), exactly as @aws-blocks/blocks injects LambdaCompute.
24
+ class StubLambdaCompute extends Compute {
25
+ fn;
26
+ apiGateway;
27
+ apiUrl;
28
+ constructor(scope, id) {
29
+ super(id, { parent: scope });
30
+ this.fn = new lambda.NodejsFunction(this, 'Handler', {
31
+ entry: this.backendHandlerPath,
32
+ runtime: cdk.aws_lambda.Runtime.NODEJS_22_X,
33
+ handler: 'handler',
34
+ role: this.executionRole,
35
+ environment: { BLOCKS_STACK_NAME: this.backendStackName },
36
+ bundling: { minify: true, esbuildArgs: { '--conditions': 'aws-runtime' } },
37
+ });
38
+ this.apiGateway = new apigateway.RestApi(this, 'API', { restApiName: 'Blocks API' });
39
+ this.apiGateway.root.addProxy({
40
+ defaultIntegration: new apigateway.LambdaIntegration(this.fn),
41
+ anyMethod: true,
42
+ });
43
+ this.apiUrl = `${this.apiGateway.url}${BLOCKS_RPC_PREFIX.slice(1)}`;
44
+ }
45
+ setEnv(key, value) {
46
+ this.fn.addEnvironment(key, value);
47
+ }
48
+ }
49
+ const stubComputeFactory = (root) => new StubLambdaCompute(root, 'DefaultCompute');
13
50
  // Simulate the CDK condition being active (tests import CDK files directly)
14
51
  before(() => {
15
52
  process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS ?? '') + ' --conditions=cdk';
@@ -21,20 +58,15 @@ const factoryBackendPath = join(__dirname, '__fixtures__', 'factory-backend.js')
21
58
  const fullIdConstructBackendPath = join(__dirname, '__fixtures__', 'fullid-construct-backend.js');
22
59
  const EXECUTION_ROLE_MARKER_ACTION = 'blocks-test:MarkerAction';
23
60
  const importMetaHandlerPath = join(__dirname, '__fixtures__', 'import-meta-handler.js');
61
+ // Wraps BlocksBackend.create, injecting the stub default-compute factory the way
62
+ // @aws-blocks/blocks injects LambdaCompute — so tests don't repeat it 15 times.
63
+ const makeBackend = (scope, id, backendCDKPath) => BlocksBackend.create(scope, id, { backendHandlerPath: handlerPath, backendCDKPath, defaults: BlocksPresets.production, defaultComputeFactory: stubComputeFactory });
24
64
  describe('ESM cache-busting (multi-stage)', () => {
25
65
  test('BlocksBackend.create() with same backendCDKPath but different IDs produces constructs in each', async () => {
26
66
  const app = new cdk.App();
27
67
  const stack = new cdk.Stack(app, 'TestStack');
28
- const backend1 = await BlocksBackend.create(stack, 'Stage1', {
29
- backendHandlerPath: handlerPath,
30
- backendCDKPath: sideEffectBackendPath,
31
- defaults: BlocksPresets.production,
32
- });
33
- const backend2 = await BlocksBackend.create(stack, 'Stage2', {
34
- backendHandlerPath: handlerPath,
35
- backendCDKPath: sideEffectBackendPath,
36
- defaults: BlocksPresets.production,
37
- });
68
+ const backend1 = await makeBackend(stack, 'Stage1', sideEffectBackendPath);
69
+ const backend2 = await makeBackend(stack, 'Stage2', sideEffectBackendPath);
38
70
  const findMarker = (scope) => scope.node.tryFindChild('SideEffectMarker');
39
71
  assert.ok(findMarker(backend1), 'Stage1 backend should have SideEffectMarker construct from module side effect');
40
72
  assert.ok(findMarker(backend2), 'Stage2 backend should have SideEffectMarker construct from re-executed module');
@@ -44,11 +76,7 @@ describe('synth shape (drop into existing stack)', () => {
44
76
  test('BlocksBackend lives inside the parent stack and synthesizes Lambda + API Gateway', async () => {
45
77
  const app = new cdk.App();
46
78
  const parent = new cdk.Stack(app, 'MyExistingStack');
47
- const backend = await BlocksBackend.create(parent, 'Blocks', {
48
- backendHandlerPath: handlerPath,
49
- backendCDKPath: sideEffectBackendPath,
50
- defaults: BlocksPresets.production,
51
- });
79
+ const backend = await makeBackend(parent, 'Blocks', sideEffectBackendPath);
52
80
  // Public surface mirrors BlocksStack.
53
81
  assert.ok(backend.handler, 'BlocksBackend should expose .handler');
54
82
  assert.ok(backend.gateway, 'BlocksBackend should expose .gateway');
@@ -62,16 +90,8 @@ describe('synth shape (drop into existing stack)', () => {
62
90
  test('multiple BlocksBackends in the same parent stack do not collide', async () => {
63
91
  const app = new cdk.App();
64
92
  const parent = new cdk.Stack(app, 'MultiBackendStack');
65
- await BlocksBackend.create(parent, 'BackendA', {
66
- backendHandlerPath: handlerPath,
67
- backendCDKPath: sideEffectBackendPath,
68
- defaults: BlocksPresets.production,
69
- });
70
- await BlocksBackend.create(parent, 'BackendB', {
71
- backendHandlerPath: handlerPath,
72
- backendCDKPath: sideEffectBackendPath,
73
- defaults: BlocksPresets.production,
74
- });
93
+ await makeBackend(parent, 'BackendA', sideEffectBackendPath);
94
+ await makeBackend(parent, 'BackendB', sideEffectBackendPath);
75
95
  const template = Template.fromStack(parent);
76
96
  template.resourceCountIs('AWS::ApiGateway::RestApi', 2);
77
97
  });
@@ -80,21 +100,13 @@ describe('shared execution role', () => {
80
100
  test('exposes executionRole on the backend', async () => {
81
101
  const app = new cdk.App();
82
102
  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
- });
103
+ const backend = await makeBackend(parent, 'Blocks', sideEffectBackendPath);
88
104
  assert.ok(backend.executionRole, 'BlocksBackend should expose .executionRole');
89
105
  });
90
106
  test('synth produces a Lambda-assumable role with basic execution, and the handler uses it', async () => {
91
107
  const app = new cdk.App();
92
108
  const parent = new cdk.Stack(app, 'RoleSynthStack');
93
- await BlocksBackend.create(parent, 'Blocks', {
94
- backendHandlerPath: handlerPath,
95
- backendCDKPath: sideEffectBackendPath,
96
- defaults: BlocksPresets.production,
97
- });
109
+ await makeBackend(parent, 'Blocks', sideEffectBackendPath);
98
110
  const template = Template.fromStack(parent);
99
111
  // The shared role (logical id derived from the 'BlocksRole' construct id)
100
112
  // is assumable by Lambda and carries AWSLambdaBasicExecutionRole (so
@@ -102,7 +114,7 @@ describe('shared execution role', () => {
102
114
  // roles exist (API Gateway CloudWatch role, config BucketDeployment role),
103
115
  // so we target ours by logical id.
104
116
  const roles = template.findResources('AWS::IAM::Role');
105
- const blocksRoleId = Object.keys(roles).find(k => k.includes('BlocksRole'));
117
+ const blocksRoleId = Object.keys(roles).find((k) => k.includes('BlocksRole'));
106
118
  assert.ok(blocksRoleId, 'expected a role from the BlocksRole construct');
107
119
  const blocksRole = roles[blocksRoleId];
108
120
  assert.deepStrictEqual(blocksRole.Properties.AssumeRolePolicyDocument.Statement[0], {
@@ -119,11 +131,7 @@ describe('shared execution role', () => {
119
131
  test('a nested block resolves executionRole via the construct-tree walk', async () => {
120
132
  const app = new cdk.App();
121
133
  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
- });
134
+ const backend = await makeBackend(parent, 'Blocks', sideEffectBackendPath);
127
135
  // Build nested Scopes under the backend (outer → inner), the same shape a
128
136
  // real Building Block tree has, and grant a uniquely-named marker action to
129
137
  // `this.executionRole` from the innermost scope. If the getter's tree-walk
@@ -160,6 +168,7 @@ describe('CJS bundle: import.meta.url in the handler is shimmed (no Lambda-load
160
168
  backendHandlerPath: importMetaHandlerPath,
161
169
  backendCDKPath: sideEffectBackendPath,
162
170
  defaults: BlocksPresets.production,
171
+ defaultComputeFactory: stubComputeFactory,
163
172
  }));
164
173
  });
165
174
  });
@@ -167,11 +176,7 @@ describe('factory function support', () => {
167
176
  test('BlocksBackend.create() calls default export function with the backend instance', async () => {
168
177
  const app = new cdk.App();
169
178
  const stack = new cdk.Stack(app, 'FactoryTestStack');
170
- const backend = await BlocksBackend.create(stack, 'FactoryStage', {
171
- backendHandlerPath: handlerPath,
172
- backendCDKPath: factoryBackendPath,
173
- defaults: BlocksPresets.production,
174
- });
179
+ const backend = await makeBackend(stack, 'FactoryStage', factoryBackendPath);
175
180
  const marker = backend.node.tryFindChild('FactoryMarker');
176
181
  assert.ok(marker, 'Factory function should have created FactoryMarker on the backend');
177
182
  });
@@ -180,11 +185,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
180
185
  test('top-level stack: fullId is {stackName}-{id} and resolvable', async () => {
181
186
  const app = new cdk.App();
182
187
  const stack = new cdk.Stack(app, 'TopLevelStack');
183
- const backend = await BlocksBackend.create(stack, 'blocks', {
184
- backendHandlerPath: handlerPath,
185
- backendCDKPath: sideEffectBackendPath,
186
- defaults: BlocksPresets.production,
187
- });
188
+ const backend = await makeBackend(stack, 'blocks', sideEffectBackendPath);
188
189
  assert.strictEqual(backend.fullId, 'TopLevelStack-blocks');
189
190
  assert.ok(!cdk.Token.isUnresolved(backend.fullId), 'fullId must not contain a token');
190
191
  });
@@ -198,11 +199,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
198
199
  const nested = new cdk.NestedStack(parent, 'blocks');
199
200
  // Sanity: the nested stack's own name really is a token.
200
201
  assert.ok(cdk.Token.isUnresolved(nested.stackName), 'precondition: NestedStack.stackName should be an unresolved token');
201
- const backend = await BlocksBackend.create(nested, 'blocks', {
202
- backendHandlerPath: handlerPath,
203
- backendCDKPath: sideEffectBackendPath,
204
- defaults: BlocksPresets.production,
205
- });
202
+ const backend = await makeBackend(nested, 'blocks', sideEffectBackendPath);
206
203
  assert.ok(!cdk.Token.isUnresolved(backend.fullId), `fullId must be token-free inside a nested stack, got: ${backend.fullId}`);
207
204
  // Falls back to the top-level (concrete) stack name, keeping uniqueness.
208
205
  assert.strictEqual(backend.fullId, 'ParentStack-blocks');
@@ -213,11 +210,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
213
210
  const app = new cdk.App();
214
211
  const parent = new cdk.Stack(app, 'Gen2ParentStack');
215
212
  const nested = new cdk.NestedStack(parent, 'blocks');
216
- const backend = await BlocksBackend.create(nested, 'blocks', {
217
- backendHandlerPath: handlerPath,
218
- backendCDKPath: fullIdConstructBackendPath,
219
- defaults: BlocksPresets.production,
220
- });
213
+ const backend = await makeBackend(nested, 'blocks', fullIdConstructBackendPath);
221
214
  // The construct ID is `${scope.fullId}Marker` → `ParentStack-blocks-blocks-dbMarker`.
222
215
  const expectedId = `${backend.fullId}-dbMarker`;
223
216
  assert.ok(nested.node.tryFindChild(expectedId), `expected a child construct with id "${expectedId}"`);
@@ -234,11 +227,7 @@ describe('fullId is token-free (construct IDs / env-var keys)', () => {
234
227
  const app = new cdk.App();
235
228
  const parent = new cdk.Stack(app, 'InvariantParent');
236
229
  const nested = new cdk.NestedStack(parent, 'blocks');
237
- const backend = await BlocksBackend.create(nested, 'blocks', {
238
- backendHandlerPath: handlerPath,
239
- backendCDKPath: sideEffectBackendPath,
240
- defaults: BlocksPresets.production,
241
- });
230
+ const backend = await makeBackend(nested, 'blocks', sideEffectBackendPath);
242
231
  const template = Template.fromStack(nested);
243
232
  const fns = template.findResources('AWS::Lambda::Function');
244
233
  const envValues = Object.values(fns)
@@ -259,11 +248,13 @@ describe('infrastructure defaults (backend-anchored)', () => {
259
248
  backendHandlerPath: handlerPath,
260
249
  backendCDKPath: sideEffectBackendPath,
261
250
  defaults: BlocksPresets.production,
251
+ defaultComputeFactory: stubComputeFactory,
262
252
  });
263
253
  const b = await BlocksBackend.create(stack, 'B', {
264
254
  backendHandlerPath: handlerPath,
265
255
  backendCDKPath: sideEffectBackendPath,
266
256
  defaults: BlocksPresets.sandbox,
257
+ defaultComputeFactory: stubComputeFactory,
267
258
  });
268
259
  // Two backends in one stack must NOT clobber each other — defaults are
269
260
  // anchored on the backend, not the shared stack.
@@ -277,6 +268,7 @@ describe('infrastructure defaults (backend-anchored)', () => {
277
268
  backendHandlerPath: handlerPath,
278
269
  backendCDKPath: sideEffectBackendPath,
279
270
  defaults: BlocksPresets.sandbox,
271
+ defaultComputeFactory: stubComputeFactory,
280
272
  });
281
273
  // A Scope under the backend resolves scope.defaults by walking up to it.
282
274
  const outer = new Scope('outer');
@@ -20,6 +20,28 @@ export interface BlocksDefaults {
20
20
  * well as the CLI, API, and AWS console — until it is turned off.
21
21
  */
22
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
+ };
23
45
  }
24
46
  /**
25
47
  * Prepared starting points for {@link BlocksDefaults}. Pick one and override
@@ -30,15 +52,19 @@ export interface BlocksDefaults {
30
52
  * ```
31
53
  */
32
54
  export declare const BlocksPresets: {
33
- /** Disposable development stacks: tear down cleanly, no delete guard. */
55
+ /** Disposable development stacks: tear down cleanly, no delete guard, no backups. */
34
56
  sandbox: {
35
57
  removalPolicy: RemovalPolicy.DESTROY;
36
58
  deletionProtection: false;
59
+ allowedOrigins: string[];
60
+ pointInTimeRecovery: false;
37
61
  };
38
62
  /** Durable, protected posture for permanent deployments. */
39
63
  production: {
40
64
  removalPolicy: RemovalPolicy.RETAIN;
41
65
  deletionProtection: true;
66
+ allowedOrigins: never[];
67
+ pointInTimeRecovery: true;
42
68
  };
43
69
  };
44
70
  //# sourceMappingURL=blocks-defaults.d.ts.map
@@ -1 +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"}
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"}
@@ -10,14 +10,18 @@ import { RemovalPolicy } from 'aws-cdk-lib';
10
10
  * ```
11
11
  */
12
12
  export const BlocksPresets = {
13
- /** Disposable development stacks: tear down cleanly, no delete guard. */
13
+ /** Disposable development stacks: tear down cleanly, no delete guard, no backups. */
14
14
  sandbox: {
15
15
  removalPolicy: RemovalPolicy.DESTROY,
16
16
  deletionProtection: false,
17
+ allowedOrigins: ['^https?://(localhost|127\\.0\\.0\\.1)(:\\d+)?$'],
18
+ pointInTimeRecovery: false,
17
19
  },
18
20
  /** Durable, protected posture for permanent deployments. */
19
21
  production: {
20
22
  removalPolicy: RemovalPolicy.RETAIN,
21
23
  deletionProtection: true,
24
+ allowedOrigins: [],
25
+ pointInTimeRecovery: true,
22
26
  },
23
27
  };
@@ -11,12 +11,14 @@ import assert from 'node:assert';
11
11
  import { RemovalPolicy } from 'aws-cdk-lib';
12
12
  import { BlocksPresets } from './blocks-defaults.js';
13
13
  describe('BlocksPresets', () => {
14
- test('sandbox is disposable: DESTROY + deletion protection off', () => {
14
+ test('sandbox is disposable: DESTROY + deletion protection off + no PITR', () => {
15
15
  assert.strictEqual(BlocksPresets.sandbox.removalPolicy, RemovalPolicy.DESTROY);
16
16
  assert.strictEqual(BlocksPresets.sandbox.deletionProtection, false);
17
+ assert.strictEqual(BlocksPresets.sandbox.pointInTimeRecovery, false);
17
18
  });
18
- test('production is durable: RETAIN + deletion protection on', () => {
19
+ test('production is durable: RETAIN + deletion protection on + PITR on', () => {
19
20
  assert.strictEqual(BlocksPresets.production.removalPolicy, RemovalPolicy.RETAIN);
20
21
  assert.strictEqual(BlocksPresets.production.deletionProtection, true);
22
+ assert.strictEqual(BlocksPresets.production.pointInTimeRecovery, true);
21
23
  });
22
24
  });