@aws-blocks/core 0.3.0 → 0.5.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 (147) hide show
  1. package/README.md +25 -0
  2. package/dist/api.d.ts.map +1 -1
  3. package/dist/api.js +32 -0
  4. package/dist/api.test.js +22 -0
  5. package/dist/bb-utils.d.ts +1 -0
  6. package/dist/bb-utils.d.ts.map +1 -1
  7. package/dist/bb-utils.js +3 -0
  8. package/dist/cdk/apigateway-account.d.ts +33 -0
  9. package/dist/cdk/apigateway-account.d.ts.map +1 -0
  10. package/dist/cdk/apigateway-account.js +60 -0
  11. package/dist/cdk/blocks-backend.d.ts +11 -0
  12. package/dist/cdk/blocks-backend.d.ts.map +1 -1
  13. package/dist/cdk/blocks-backend.js +72 -13
  14. package/dist/cdk/blocks-backend.test.js +88 -1
  15. package/dist/cdk/blocks-defaults.d.ts +76 -1
  16. package/dist/cdk/blocks-defaults.d.ts.map +1 -1
  17. package/dist/cdk/blocks-defaults.js +17 -1
  18. package/dist/cdk/blocks-defaults.test.js +17 -0
  19. package/dist/cdk/blocks-stack.test.js +44 -3
  20. package/dist/cdk/compute/compute-registry.d.ts +19 -0
  21. package/dist/cdk/compute/compute-registry.d.ts.map +1 -0
  22. package/dist/cdk/compute/compute-registry.js +38 -0
  23. package/dist/cdk/compute/compute.d.ts +82 -2
  24. package/dist/cdk/compute/compute.d.ts.map +1 -1
  25. package/dist/cdk/compute/compute.js +64 -2
  26. package/dist/cdk/compute/default-compute-factory.d.ts +1 -0
  27. package/dist/cdk/compute/default-compute-factory.d.ts.map +1 -1
  28. package/dist/cdk/config-registry.d.ts +34 -4
  29. package/dist/cdk/config-registry.d.ts.map +1 -1
  30. package/dist/cdk/config-registry.js +83 -25
  31. package/dist/cdk/config-registry.test.d.ts +2 -0
  32. package/dist/cdk/config-registry.test.d.ts.map +1 -0
  33. package/dist/cdk/config-registry.test.js +127 -0
  34. package/dist/cdk/dashboard-registry.d.ts +41 -0
  35. package/dist/cdk/dashboard-registry.d.ts.map +1 -0
  36. package/dist/cdk/dashboard-registry.js +61 -0
  37. package/dist/cdk/index.d.ts +90 -6
  38. package/dist/cdk/index.d.ts.map +1 -1
  39. package/dist/cdk/index.js +135 -13
  40. package/dist/cdk/internal.d.ts +3 -1
  41. package/dist/cdk/internal.d.ts.map +1 -1
  42. package/dist/cdk/internal.js +4 -1
  43. package/dist/cdk/tracer-registry.d.ts +31 -0
  44. package/dist/cdk/tracer-registry.d.ts.map +1 -0
  45. package/dist/cdk/tracer-registry.js +49 -0
  46. package/dist/cdk/vpc-requirements-registry.d.ts +33 -0
  47. package/dist/cdk/vpc-requirements-registry.d.ts.map +1 -0
  48. package/dist/cdk/vpc-requirements-registry.js +46 -0
  49. package/dist/cdk/vpc-types.d.ts +151 -0
  50. package/dist/cdk/vpc-types.d.ts.map +1 -0
  51. package/dist/cdk/vpc-types.js +3 -0
  52. package/dist/cdk/vpc.d.ts +59 -0
  53. package/dist/cdk/vpc.d.ts.map +1 -0
  54. package/dist/cdk/vpc.js +298 -0
  55. package/dist/cdk/vpc.test.d.ts +2 -0
  56. package/dist/cdk/vpc.test.d.ts.map +1 -0
  57. package/dist/cdk/vpc.test.js +285 -0
  58. package/dist/client/index.d.ts +1 -1
  59. package/dist/client/index.d.ts.map +1 -1
  60. package/dist/client/index.js +1 -1
  61. package/dist/common/config.d.ts +34 -0
  62. package/dist/common/config.d.ts.map +1 -1
  63. package/dist/common/config.js +45 -3
  64. package/dist/common/config.test.js +19 -0
  65. package/dist/common/index.d.ts +8 -0
  66. package/dist/common/index.d.ts.map +1 -1
  67. package/dist/errors.d.ts +21 -0
  68. package/dist/errors.d.ts.map +1 -1
  69. package/dist/errors.js +25 -0
  70. package/dist/hosting.d.ts +9 -0
  71. package/dist/hosting.d.ts.map +1 -1
  72. package/dist/hosting.js +12 -1
  73. package/dist/hosting.test.js +63 -1
  74. package/dist/index.cdk.d.ts +3 -2
  75. package/dist/index.cdk.d.ts.map +1 -1
  76. package/dist/index.cdk.js +2 -2
  77. package/dist/index.d.ts +1 -1
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +1 -1
  80. package/dist/lambda-handler.d.ts +17 -0
  81. package/dist/lambda-handler.d.ts.map +1 -1
  82. package/dist/lambda-handler.js +61 -5
  83. package/dist/lambda-handler.test.js +191 -2
  84. package/dist/raw-route.d.ts +15 -1
  85. package/dist/raw-route.d.ts.map +1 -1
  86. package/dist/raw-route.js +96 -12
  87. package/dist/raw-route.test.js +332 -1
  88. package/dist/scripts/dev-server.d.ts.map +1 -1
  89. package/dist/scripts/dev-server.js +11 -0
  90. package/dist/scripts/extract-ts-types.d.ts.map +1 -1
  91. package/dist/scripts/extract-ts-types.js +107 -23
  92. package/dist/scripts/extract-ts-types.test.js +225 -26
  93. package/dist/scripts/generate-spec.d.ts.map +1 -1
  94. package/dist/scripts/generate-spec.js +14 -5
  95. package/dist/scripts/generate-spec.test.js +93 -0
  96. package/dist/scripts/sandbox-empty-buckets.test.d.ts +2 -0
  97. package/dist/scripts/sandbox-empty-buckets.test.d.ts.map +1 -0
  98. package/dist/scripts/sandbox-empty-buckets.test.js +171 -0
  99. package/dist/scripts/sandbox.d.ts +54 -0
  100. package/dist/scripts/sandbox.d.ts.map +1 -1
  101. package/dist/scripts/sandbox.js +163 -25
  102. package/dist/version.d.ts +1 -1
  103. package/dist/version.js +1 -1
  104. package/package.json +10 -1
  105. package/src/api.test.ts +25 -0
  106. package/src/api.ts +39 -0
  107. package/src/bb-utils.ts +3 -0
  108. package/src/cdk/apigateway-account.ts +66 -0
  109. package/src/cdk/blocks-backend.test.ts +168 -60
  110. package/src/cdk/blocks-backend.ts +299 -227
  111. package/src/cdk/blocks-defaults.test.ts +21 -0
  112. package/src/cdk/blocks-defaults.ts +79 -1
  113. package/src/cdk/blocks-stack.test.ts +57 -13
  114. package/src/cdk/compute/compute-registry.ts +45 -0
  115. package/src/cdk/compute/compute.ts +114 -2
  116. package/src/cdk/compute/default-compute-factory.ts +1 -0
  117. package/src/cdk/config-registry.test.ts +149 -0
  118. package/src/cdk/config-registry.ts +92 -34
  119. package/src/cdk/dashboard-registry.ts +68 -0
  120. package/src/cdk/index.ts +427 -256
  121. package/src/cdk/internal.ts +6 -2
  122. package/src/cdk/tracer-registry.ts +54 -0
  123. package/src/cdk/vpc-requirements-registry.ts +63 -0
  124. package/src/cdk/vpc-types.ts +158 -0
  125. package/src/cdk/vpc.test.ts +348 -0
  126. package/src/cdk/vpc.ts +336 -0
  127. package/src/client/index.ts +1 -1
  128. package/src/common/config.test.ts +21 -0
  129. package/src/common/config.ts +47 -3
  130. package/src/common/index.ts +8 -0
  131. package/src/errors.ts +26 -0
  132. package/src/hosting.test.ts +87 -1
  133. package/src/hosting.ts +24 -1
  134. package/src/index.cdk.ts +11 -1
  135. package/src/index.ts +1 -1
  136. package/src/lambda-handler.test.ts +220 -2
  137. package/src/lambda-handler.ts +65 -4
  138. package/src/raw-route.test.ts +427 -1
  139. package/src/raw-route.ts +125 -12
  140. package/src/scripts/dev-server.ts +12 -1
  141. package/src/scripts/extract-ts-types.test.ts +228 -26
  142. package/src/scripts/extract-ts-types.ts +104 -20
  143. package/src/scripts/generate-spec.test.ts +101 -0
  144. package/src/scripts/generate-spec.ts +15 -5
  145. package/src/scripts/sandbox-empty-buckets.test.ts +191 -0
  146. package/src/scripts/sandbox.ts +185 -24
  147. package/src/version.ts +1 -1
@@ -4,14 +4,19 @@
4
4
  import * as cdk from 'aws-cdk-lib';
5
5
  import * as s3 from 'aws-cdk-lib/aws-s3';
6
6
  import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
7
- import * as iam from 'aws-cdk-lib/aws-iam';
8
7
  import type { Construct } from 'constructs';
8
+ import type { Compute } from './compute/compute.js';
9
9
 
10
10
  const REGISTRY_KEY = Symbol.for('BLOCKS_CONFIG_REGISTRY');
11
11
 
12
+ /** The object key of the config JSON under {@link getConfigLocation}'s bucket. */
13
+ const CONFIG_KEY = 'blocks-config.json';
14
+
12
15
  interface ConfigRegistryState {
13
16
  entries: Map<string, unknown>;
14
17
  finalized: boolean;
18
+ /** The shared config bucket, created once per stack by {@link getConfigLocation}. */
19
+ bucket?: s3.Bucket;
15
20
  }
16
21
 
17
22
  /**
@@ -45,62 +50,115 @@ export function registerConfig(scope: Construct, key: string, value: unknown): v
45
50
  registry.entries.set(key, value);
46
51
  }
47
52
 
53
+ /**
54
+ * Ensure the shared config bucket exists and return where the config JSON lives
55
+ * (`{ bucketName, key }`). The bucket is created **once per stack** (memoized on
56
+ * the registry) and this is idempotent — the first caller creates it, later
57
+ * callers get the same bucket regardless of order.
58
+ *
59
+ * Any compute that loads config at runtime (`loadConfigToProcessEnv()`) injects
60
+ * these two values as `BLOCKS_CONFIG_BUCKET` / `BLOCKS_CONFIG_KEY`. The Lambda
61
+ * handler gets them from {@link finalizeConfigRegistry}; other compute that runs
62
+ * as the shared execution role (e.g. the Agent BB's AgentCore Runtime) calls this
63
+ * at construction to inject them too, so it loads the same app config the handler
64
+ * does. IAM is not granted here — `finalizeConfigRegistry` grants read on the config
65
+ * object to the shared execution role, which such compute inherits.
66
+ *
67
+ * @param scope - Any construct in the stack; the bucket is created under the stack.
68
+ */
69
+ export function getConfigLocation(scope: Construct): { bucketName: string; key: string } {
70
+ return { bucketName: ensureConfigBucket(scope).bucketName, key: CONFIG_KEY };
71
+ }
72
+
73
+ /**
74
+ * Create-or-return the shared config bucket (memoized on the per-stack registry). Created under the
75
+ * owning `BlocksStack`/`BlocksBackend` (`globalThis.CURRENT_BLOCKS_STACK` — the construct finalize
76
+ * historically used), so its logical ID is stable regardless of which caller creates it first: a
77
+ * co-located BB (e.g. the AgentCore Runtime, a deep construct) may be the first to call it, and a
78
+ * `BlocksBackend` embedded in a customer stack must keep `Blocks/BlocksConfigBucket` (no replacement).
79
+ * Falls back to the stack when no owner is registered (isolated unit tests). Returns a concrete
80
+ * `s3.Bucket` so callers don't need a non-null assertion.
81
+ */
82
+ function ensureConfigBucket(scope: Construct): s3.Bucket {
83
+ const stack = cdk.Stack.of(scope);
84
+ const registry = getRegistry(stack);
85
+ if (!registry.bucket) {
86
+ const owner = ((globalThis as any).CURRENT_BLOCKS_STACK as Construct | undefined) ?? stack;
87
+ registry.bucket = new s3.Bucket(owner, 'BlocksConfigBucket', {
88
+ removalPolicy: cdk.RemovalPolicy.DESTROY,
89
+ autoDeleteObjects: true,
90
+ encryption: s3.BucketEncryption.S3_MANAGED,
91
+ blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
92
+ lifecycleRules: [
93
+ { noncurrentVersionExpiration: cdk.Duration.days(1) },
94
+ ],
95
+ });
96
+ }
97
+ return registry.bucket;
98
+ }
99
+
48
100
  /**
49
101
  * Finalize the config registry: create an S3 bucket, upload the config JSON,
50
- * set env vars on the handler, and grant read access.
102
+ * grant read to the shared execution role, and stamp the config coordinates
103
+ * (`BLOCKS_CONFIG_BUCKET` / `BLOCKS_CONFIG_KEY`) onto every compute.
104
+ *
105
+ * Read access is granted once to the shared role (`root.executionRole`) rather
106
+ * than to a single function, so every compute that assumes the role can read
107
+ * the object. The bucket/key coordinates can't live on a role (env vars are
108
+ * per-compute), so they are set on each compute via `setEnv`.
51
109
  *
52
110
  * Must be called after all BBs are constructed (i.e., after the backendCDKPath
53
111
  * import completes in BlocksStack.create() / BlocksBackend.create()).
54
112
  *
55
- * @param scope - The CDK construct to create resources under
56
- * @param handler - The Lambda function that needs to read the config
113
+ * @param root - The construct to create the config resources under (also used
114
+ * to locate the owning stack).
115
+ * @param executionRole - The shared role every compute assumes; config read is
116
+ * granted to it once.
117
+ * @param computes - The computes to stamp `BLOCKS_CONFIG_BUCKET` / `BLOCKS_CONFIG_KEY` on.
57
118
  */
58
119
  export function finalizeConfigRegistry(
59
- scope: Construct,
60
- handler: cdk.aws_lambda.IFunction,
120
+ root: Construct,
121
+ executionRole: cdk.aws_iam.IRole,
122
+ computes: readonly Compute[],
61
123
  ): void {
62
- const stack = cdk.Stack.of(scope);
124
+ const stack = cdk.Stack.of(root);
63
125
  const registry = getRegistry(stack);
64
126
 
65
127
  if (registry.finalized) return;
66
128
  registry.finalized = true;
67
129
 
68
- if (registry.entries.size === 0) return;
69
-
70
- const configBucket = new s3.Bucket(scope, 'BlocksConfigBucket', {
71
- removalPolicy: cdk.RemovalPolicy.DESTROY,
72
- autoDeleteObjects: true,
73
- encryption: s3.BucketEncryption.S3_MANAGED,
74
- blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
75
- lifecycleRules: [
76
- { noncurrentVersionExpiration: cdk.Duration.days(1) },
77
- ],
78
- });
130
+ // Nothing to do only if no config was registered AND no bucket was created (via
131
+ // getConfigLocation). If a co-located BB created the bucket, still upload (even an empty {}) and
132
+ // wire the handler so that compute's loadConfigToProcessEnv() resolves instead of 404-ing forever.
133
+ if (registry.entries.size === 0 && !registry.bucket) return;
79
134
 
80
- const configKey = 'blocks-config.json';
135
+ // Ensure the bucket exists (a co-located BB may already have created it via getConfigLocation).
136
+ const configBucket = ensureConfigBucket(root);
137
+ const configKey = CONFIG_KEY;
81
138
 
82
139
  const configObject = cdk.Lazy.any({
83
140
  produce: () => Object.fromEntries(registry.entries),
84
141
  });
85
142
 
86
- const deployment = new s3deploy.BucketDeployment(scope, 'BlocksConfigDeployment', {
143
+ new s3deploy.BucketDeployment(root, 'BlocksConfigDeployment', {
87
144
  sources: [s3deploy.Source.jsonData(configKey, configObject)],
88
145
  destinationBucket: configBucket,
89
146
  prune: false,
90
147
  });
91
148
 
92
- (handler as cdk.aws_lambda.Function).addEnvironment(
93
- 'BLOCKS_CONFIG_BUCKET',
94
- configBucket.bucketName,
95
- );
96
- (handler as cdk.aws_lambda.Function).addEnvironment(
97
- 'BLOCKS_CONFIG_KEY',
98
- configKey,
99
- );
100
-
101
- // Scope IAM grant to the specific config key
102
- (handler as cdk.aws_lambda.Function).addToRolePolicy(new iam.PolicyStatement({
103
- actions: ['s3:GetObject'],
104
- resources: [`${configBucket.bucketArn}/${configKey}`],
105
- }));
149
+ // Grant read once to the shared role (scoped to the config key), so every
150
+ // compute assuming the role can read it. We intentionally use `grantRead`
151
+ // (which also adds s3:GetBucket*/s3:List* alongside s3:GetObject*) rather
152
+ // than a hand-rolled GetObject-only statement: this is a dedicated,
153
+ // block-all-public, config-only bucket, so the broader action set carries
154
+ // negligible exposure, and grantRead stays correct automatically if the
155
+ // bucket ever moves to KMS encryption (it would add kms:Decrypt).
156
+ configBucket.grantRead(executionRole, configKey);
157
+
158
+ // Stamp the config coordinates on every compute — env vars can't live on a
159
+ // role, so each compute needs them to locate the object at runtime.
160
+ for (const compute of computes) {
161
+ compute.setEnv('BLOCKS_CONFIG_BUCKET', configBucket.bucketName);
162
+ compute.setEnv('BLOCKS_CONFIG_KEY', configKey);
163
+ }
106
164
  }
@@ -0,0 +1,68 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import * as cdk from 'aws-cdk-lib';
4
+ import type { Construct } from 'constructs';
5
+
6
+ const REGISTRY_KEY = Symbol.for('BLOCKS_DASHBOARD_REGISTRY');
7
+
8
+ /** A Dashboard's deferred widget-body build, run once after the app is constructed. */
9
+ type DashboardFinalizer = () => void;
10
+
11
+ /**
12
+ * Get or create the deferred-dashboard list for a given stack. Stored on the
13
+ * stack object (keyed by a Symbol), so each stack in a multi-stack synth gets
14
+ * its own — mirrors the config + compute registries.
15
+ */
16
+ function getRegistry(stack: cdk.Stack): DashboardFinalizer[] {
17
+ let list = (stack as unknown as Record<symbol, DashboardFinalizer[] | undefined>)[REGISTRY_KEY];
18
+ if (!list) {
19
+ list = [];
20
+ (stack as unknown as Record<symbol, DashboardFinalizer[]>)[REGISTRY_KEY] = list;
21
+ }
22
+ return list;
23
+ }
24
+
25
+ /**
26
+ * Register a Dashboard's deferred body-build, to run after every Building Block
27
+ * in the app has been constructed (the end of `BlocksStack`/`BlocksBackend`
28
+ * `create()`, once the backend module has fully imported).
29
+ *
30
+ * The Dashboard builds its widget body here rather than in its constructor
31
+ * because the body depends on which computes are traced, and a `Tracer` may be
32
+ * constructed *after* the Dashboard. Deferring makes the Dashboard observe the
33
+ * complete app, so it never depends on construction order. (The Dashboard's
34
+ * CloudWatch resource itself is created eagerly in the constructor, so its URL /
35
+ * redirect route never dangle — only the body is deferred.)
36
+ *
37
+ * This is intentionally scoped to the Dashboard (the only deferred-build case
38
+ * today) rather than a generic finalizer mechanism; generalize it only if a
39
+ * second use case appears.
40
+ *
41
+ * @param scope - Any construct in the stack (used to locate the stack).
42
+ * @param finalize - The deferred body-build; run once (in registration order)
43
+ * by {@link finalizeDashboards}.
44
+ */
45
+ export function registerDashboardFinalizer(scope: Construct, finalize: DashboardFinalizer): void {
46
+ getRegistry(cdk.Stack.of(scope)).push(finalize);
47
+ }
48
+
49
+ /**
50
+ * Run — and clear — every registered Dashboard finalizer on `scope`'s stack, in
51
+ * registration order. Called once from `create()` after the backend module has
52
+ * imported. Clearing the list makes a repeated call a no-op, so a dashboard's
53
+ * body is never built twice.
54
+ *
55
+ * A Dashboard constructed outside `create()` (e.g. directly in a unit test) must
56
+ * call this explicitly before synth — the same way `config-registry.test.ts`
57
+ * drives `finalizeConfigRegistry`. (A Dashboard's CloudWatch resource is created
58
+ * eagerly in its constructor, so even if its finalizer never runs its URL /
59
+ * redirect never dangle — only the widget body is missing.)
60
+ *
61
+ * @param scope - Any construct in the stack (used to locate the stack).
62
+ */
63
+ export function finalizeDashboards(scope: Construct): void {
64
+ const list = getRegistry(cdk.Stack.of(scope));
65
+ // Drain the list so a repeated call can't rebuild an already-built dashboard.
66
+ const pending = list.splice(0, list.length);
67
+ for (const finalize of pending) finalize();
68
+ }