@aws-blocks/core 0.2.0 → 0.4.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 (178) hide show
  1. package/README.md +13 -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 +43 -9
  12. package/dist/cdk/blocks-backend.d.ts.map +1 -1
  13. package/dist/cdk/blocks-backend.js +69 -73
  14. package/dist/cdk/blocks-backend.test.js +78 -69
  15. package/dist/cdk/blocks-defaults.d.ts +92 -2
  16. package/dist/cdk/blocks-defaults.d.ts.map +1 -1
  17. package/dist/cdk/blocks-defaults.js +22 -2
  18. package/dist/cdk/blocks-defaults.test.js +21 -2
  19. package/dist/cdk/blocks-stack.test.js +94 -37
  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 +2 -0
  24. package/dist/cdk/compute/compute.d.ts.map +1 -1
  25. package/dist/cdk/compute/compute.js +8 -0
  26. package/dist/cdk/compute/default-compute-factory.d.ts +34 -0
  27. package/dist/cdk/compute/default-compute-factory.d.ts.map +1 -0
  28. package/dist/cdk/compute/default-compute-factory.js +3 -0
  29. package/dist/cdk/config-registry.d.ts +34 -4
  30. package/dist/cdk/config-registry.d.ts.map +1 -1
  31. package/dist/cdk/config-registry.js +83 -25
  32. package/dist/cdk/config-registry.test.d.ts +2 -0
  33. package/dist/cdk/config-registry.test.d.ts.map +1 -0
  34. package/dist/cdk/config-registry.test.js +115 -0
  35. package/dist/cdk/index.d.ts +76 -7
  36. package/dist/cdk/index.d.ts.map +1 -1
  37. package/dist/cdk/index.js +99 -9
  38. package/dist/cdk/internal.d.ts +1 -0
  39. package/dist/cdk/internal.d.ts.map +1 -1
  40. package/dist/client/index.d.ts +1 -1
  41. package/dist/client/index.d.ts.map +1 -1
  42. package/dist/client/index.js +1 -1
  43. package/dist/common/config.d.ts +34 -0
  44. package/dist/common/config.d.ts.map +1 -1
  45. package/dist/common/config.js +45 -3
  46. package/dist/common/config.test.js +19 -0
  47. package/dist/common/index.d.ts +8 -0
  48. package/dist/common/index.d.ts.map +1 -1
  49. package/dist/errors.d.ts +16 -0
  50. package/dist/errors.d.ts.map +1 -1
  51. package/dist/errors.js +20 -0
  52. package/dist/hosting-secrets.d.ts +48 -0
  53. package/dist/hosting-secrets.d.ts.map +1 -0
  54. package/dist/hosting-secrets.js +57 -0
  55. package/dist/hosting-secrets.test.d.ts +2 -0
  56. package/dist/hosting-secrets.test.d.ts.map +1 -0
  57. package/dist/hosting-secrets.test.js +81 -0
  58. package/dist/hosting.d.ts +97 -6
  59. package/dist/hosting.d.ts.map +1 -1
  60. package/dist/hosting.js +91 -23
  61. package/dist/hosting.test.js +182 -10
  62. package/dist/index.cdk.d.ts +13 -12
  63. package/dist/index.cdk.d.ts.map +1 -1
  64. package/dist/index.cdk.js +13 -9
  65. package/dist/index.d.ts +8 -7
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +9 -5
  68. package/dist/lambda-handler.d.ts +17 -0
  69. package/dist/lambda-handler.d.ts.map +1 -1
  70. package/dist/lambda-handler.js +52 -3
  71. package/dist/lambda-handler.test.js +196 -1
  72. package/dist/pipeline/index.d.ts +24 -2
  73. package/dist/pipeline/index.d.ts.map +1 -1
  74. package/dist/pipeline/index.js +41 -1
  75. package/dist/pipeline.test.d.ts +2 -0
  76. package/dist/pipeline.test.d.ts.map +1 -0
  77. package/dist/pipeline.test.js +47 -0
  78. package/dist/rpc.d.ts +21 -0
  79. package/dist/rpc.d.ts.map +1 -1
  80. package/dist/rpc.js +34 -0
  81. package/dist/rpc.test.js +38 -1
  82. package/dist/scripts/config.d.ts +15 -0
  83. package/dist/scripts/config.d.ts.map +1 -0
  84. package/dist/scripts/config.js +29 -0
  85. package/dist/scripts/deploy.d.ts.map +1 -1
  86. package/dist/scripts/deploy.js +4 -0
  87. package/dist/scripts/dev-server.d.ts +8 -0
  88. package/dist/scripts/dev-server.d.ts.map +1 -1
  89. package/dist/scripts/dev-server.js +31 -0
  90. package/dist/scripts/generate-client.d.ts.map +1 -1
  91. package/dist/scripts/generate-client.js +9 -4
  92. package/dist/scripts/generate-client.test.js +19 -4
  93. package/dist/scripts/index.d.ts +13 -10
  94. package/dist/scripts/index.d.ts.map +1 -1
  95. package/dist/scripts/index.js +11 -8
  96. package/dist/scripts/preflight-credentials.d.ts +30 -0
  97. package/dist/scripts/preflight-credentials.d.ts.map +1 -0
  98. package/dist/scripts/preflight-credentials.js +108 -0
  99. package/dist/scripts/preflight-credentials.test.d.ts +2 -0
  100. package/dist/scripts/preflight-credentials.test.d.ts.map +1 -0
  101. package/dist/scripts/preflight-credentials.test.js +77 -0
  102. package/dist/scripts/sandbox-args.test.d.ts +2 -0
  103. package/dist/scripts/sandbox-args.test.d.ts.map +1 -0
  104. package/dist/scripts/sandbox-args.test.js +77 -0
  105. package/dist/scripts/sandbox-empty-buckets.test.d.ts +2 -0
  106. package/dist/scripts/sandbox-empty-buckets.test.d.ts.map +1 -0
  107. package/dist/scripts/sandbox-empty-buckets.test.js +171 -0
  108. package/dist/scripts/sandbox.d.ts +106 -0
  109. package/dist/scripts/sandbox.d.ts.map +1 -1
  110. package/dist/scripts/sandbox.js +255 -40
  111. package/dist/scripts/secret.d.ts +15 -0
  112. package/dist/scripts/secret.d.ts.map +1 -0
  113. package/dist/scripts/secret.js +29 -0
  114. package/dist/scripts/secret.test.d.ts +2 -0
  115. package/dist/scripts/secret.test.d.ts.map +1 -0
  116. package/dist/scripts/secret.test.js +28 -0
  117. package/dist/scripts/typegen.d.ts +15 -0
  118. package/dist/scripts/typegen.d.ts.map +1 -0
  119. package/dist/scripts/typegen.js +16 -0
  120. package/dist/secret-naming.d.ts +58 -0
  121. package/dist/secret-naming.d.ts.map +1 -0
  122. package/dist/secret-naming.js +67 -0
  123. package/dist/secret-naming.test.d.ts +2 -0
  124. package/dist/secret-naming.test.d.ts.map +1 -0
  125. package/dist/secret-naming.test.js +40 -0
  126. package/dist/version.d.ts +1 -1
  127. package/dist/version.js +1 -1
  128. package/package.json +4 -1
  129. package/src/api.test.ts +25 -0
  130. package/src/api.ts +39 -0
  131. package/src/bb-utils.ts +3 -0
  132. package/src/cdk/apigateway-account.ts +66 -0
  133. package/src/cdk/blocks-backend.test.ts +291 -272
  134. package/src/cdk/blocks-backend.ts +88 -83
  135. package/src/cdk/blocks-defaults.test.ts +25 -2
  136. package/src/cdk/blocks-defaults.ts +94 -2
  137. package/src/cdk/blocks-stack.test.ts +186 -118
  138. package/src/cdk/compute/compute-registry.ts +45 -0
  139. package/src/cdk/compute/compute.ts +10 -0
  140. package/src/cdk/compute/default-compute-factory.ts +38 -0
  141. package/src/cdk/config-registry.test.ts +135 -0
  142. package/src/cdk/config-registry.ts +92 -34
  143. package/src/cdk/index.ts +129 -10
  144. package/src/cdk/internal.ts +1 -0
  145. package/src/client/index.ts +1 -1
  146. package/src/common/config.test.ts +21 -0
  147. package/src/common/config.ts +47 -3
  148. package/src/common/index.ts +8 -0
  149. package/src/errors.ts +21 -0
  150. package/src/hosting-secrets.test.ts +106 -0
  151. package/src/hosting-secrets.ts +106 -0
  152. package/src/hosting.test.ts +272 -63
  153. package/src/hosting.ts +200 -68
  154. package/src/index.cdk.ts +80 -38
  155. package/src/index.ts +35 -16
  156. package/src/lambda-handler.test.ts +215 -1
  157. package/src/lambda-handler.ts +54 -2
  158. package/src/pipeline/index.ts +64 -11
  159. package/src/pipeline.test.ts +55 -0
  160. package/src/rpc.test.ts +44 -1
  161. package/src/rpc.ts +41 -0
  162. package/src/scripts/config.ts +35 -0
  163. package/src/scripts/deploy.ts +5 -0
  164. package/src/scripts/dev-server.ts +40 -0
  165. package/src/scripts/generate-client.test.ts +43 -8
  166. package/src/scripts/generate-client.ts +70 -65
  167. package/src/scripts/index.ts +20 -17
  168. package/src/scripts/preflight-credentials.test.ts +110 -0
  169. package/src/scripts/preflight-credentials.ts +131 -0
  170. package/src/scripts/sandbox-args.test.ts +92 -0
  171. package/src/scripts/sandbox-empty-buckets.test.ts +191 -0
  172. package/src/scripts/sandbox.ts +287 -39
  173. package/src/scripts/secret.test.ts +35 -0
  174. package/src/scripts/secret.ts +35 -0
  175. package/src/scripts/typegen.ts +18 -0
  176. package/src/secret-naming.test.ts +59 -0
  177. package/src/secret-naming.ts +99 -0
  178. package/src/version.ts +1 -1
package/src/hosting.ts CHANGED
@@ -1,42 +1,38 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import * as cdk from 'aws-cdk-lib';
5
- import {
6
- AllowedMethods,
7
- CachePolicy,
8
- OriginRequestPolicy,
9
- ViewerProtocolPolicy,
10
- } from 'aws-cdk-lib/aws-cloudfront';
11
- import { HttpOrigin } from 'aws-cdk-lib/aws-cloudfront-origins';
12
- import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
13
- import { Construct } from 'constructs';
14
4
  import { execSync } from 'node:child_process';
15
5
  import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
16
6
  import { join, resolve } from 'node:path';
17
-
7
+ import type { DeployManifest, FrameworkType, KindStoreOptions, RouteBehavior } from '@aws-blocks/hosting/constructs';
8
+ import { detectFramework, type FrameworkAdapterFn, getAdapter, normalizeBasePath } from '@aws-blocks/hosting/adapters';
18
9
  import {
19
- HostingConstruct,
20
10
  generateBuildId,
11
+ HostingConstruct,
21
12
  type HostingConstructProps,
22
13
  type HostingDomainConfig,
23
14
  type HostingWafConfig,
24
15
  type SkewProtectionConfig,
25
16
  } from '@aws-blocks/hosting/constructs';
26
- import {
27
- detectFramework,
28
- getAdapter,
29
- normalizeBasePath,
30
- type FrameworkAdapterFn,
31
- } from '@aws-blocks/hosting/adapters';
32
- import type {
33
- DeployManifest,
34
- RouteBehavior,
35
- FrameworkType,
36
- } from '@aws-blocks/hosting';
37
- import { BLOCKS_RPC_PREFIX, BLOCKS_AUTH_PREFIX } from './constants.js';
38
- import { BLOCKS_SANDBOX_DIR } from './common/constants.js';
17
+ import * as cdk from 'aws-cdk-lib';
18
+ import { AllowedMethods, CachePolicy, OriginRequestPolicy, ViewerProtocolPolicy } from 'aws-cdk-lib/aws-cloudfront';
19
+ import { HttpOrigin } from 'aws-cdk-lib/aws-cloudfront-origins';
20
+ import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
21
+ import { Construct } from 'constructs';
39
22
  import { registerConfig } from './cdk/config-registry.js';
23
+ import { BLOCKS_SANDBOX_DIR } from './common/constants.js';
24
+ import { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
25
+ import {
26
+ assertMarkersExistAtSynth,
27
+ collectSynthMarkers,
28
+ type DomainNameInput,
29
+ type EnvValue,
30
+ partitionEnvironment,
31
+ resolveDomainNames,
32
+ resolveSecretsAtSynth,
33
+ wireByo,
34
+ wireManagedValue,
35
+ } from './hosting-secrets.js';
40
36
  import { getRegisteredRoutes } from './raw-route.js';
41
37
 
42
38
  // ─── Public types ────────────────────────────────────────────────
@@ -102,6 +98,19 @@ export type ComputeConfig = {
102
98
 
103
99
  export type { FrameworkType, HostingDomainConfig, HostingWafConfig, SkewProtectionConfig };
104
100
 
101
+ /**
102
+ * Custom-domain config for the Blocks {@link Hosting} block — the leaf
103
+ * {@link HostingDomainConfig} with `domainName` widened to also accept a
104
+ * `config()` marker (resolved to a literal at synth). Only `config()` or a plain
105
+ * string is accepted here, never `secret()` (a synth-inlined value would leak into
106
+ * the template). All other fields (`hostedZone`, `certificate`, `wwwRedirect`, …)
107
+ * are inherited unchanged from {@link HostingDomainConfig}.
108
+ */
109
+ export interface HostingDomain extends Omit<HostingDomainConfig, 'domainName'> {
110
+ /** Domain name(s): a plain string / string[] and/or a `config()` marker resolved at synth. */
111
+ domainName: DomainNameInput;
112
+ }
113
+
105
114
  /**
106
115
  * Structural interface for the Blocks backend stack.
107
116
  *
@@ -199,8 +208,55 @@ export interface HostingProps {
199
208
  /** Lambda compute configuration for SSR frameworks. */
200
209
  compute?: ComputeConfig;
201
210
 
202
- /** Custom domain configuration. */
203
- domain?: HostingDomainConfig;
211
+ /**
212
+ * Custom environment variables injected into all compute Lambda functions.
213
+ *
214
+ * Values are either plain strings or {@link secret} references:
215
+ *
216
+ * ```ts
217
+ * environment: {
218
+ * FEATURE_FLAG: 'on', // plaintext (in CFN template + Lambda config)
219
+ * STRIPE_KEY: secret('STRIPE_KEY'), // encrypted; read at runtime via getSecret()
220
+ * }
221
+ * ```
222
+ *
223
+ * A plain string is injected verbatim (visible in the CloudFormation
224
+ * template — safe for non-sensitive literals). A `secret('K')` value (AWS
225
+ * Secrets Manager) or `config('K')` value (SSM Parameter Store) injects only
226
+ * the store LOCATOR — never the value — and grants the compute role scoped
227
+ * read + decrypt; fetch it at runtime with `await getSecret('K')` /
228
+ * `await getConfig('K')`. A bring-your-own CDK `ISecret` / `IParameter` handle
229
+ * is accepted too and resolves identically.
230
+ *
231
+ * The env-var name must equal the key: `STRIPE_KEY: secret('STRIPE_KEY')`.
232
+ */
233
+ environment?: Record<string, EnvValue>;
234
+
235
+ /**
236
+ * Custom domain configuration. `domainName` accepts plain strings and/or
237
+ * {@link config} references (e.g. `domain: { domainName: config('DOMAIN_PROD') }`).
238
+ * A domain is resolved at synth time and inlined as a literal into the template,
239
+ * so only `config()` (non-sensitive → SSM) is allowed here, never `secret()`
240
+ * (which would leak into the template). Any domain marker requires constructing
241
+ * via the async {@link Hosting.create}.
242
+ */
243
+ domain?: HostingDomain;
244
+
245
+ /**
246
+ * Namespace/cache config for `secret()` values (AWS Secrets Manager). Defaults
247
+ * to the Blocks `/blocks/secrets` prefix; set `prefix` to namespace per app,
248
+ * `stage` for per-stage values with a shared fallback, or `cacheTtlSeconds` to
249
+ * refresh a rotated value on a warm compute without a cold start. Any field the
250
+ * app sets overrides the Blocks default.
251
+ */
252
+ secretStore?: KindStoreOptions;
253
+
254
+ /**
255
+ * Namespace/cache config for `config()` values (SSM Parameter Store). Defaults
256
+ * to the Blocks `/blocks/config` prefix; same override semantics as
257
+ * {@link HostingProps.secretStore}.
258
+ */
259
+ configStore?: KindStoreOptions;
204
260
 
205
261
  /** WAF protection configuration. */
206
262
  waf?: HostingWafConfig;
@@ -208,6 +264,16 @@ export interface HostingProps {
208
264
  /** Retain the S3 bucket when the stack is deleted. Default: false. */
209
265
  retainOnDelete?: boolean;
210
266
 
267
+ /**
268
+ * Days to retain SUPERSEDED build artifacts in S3 before they are expired.
269
+ * Default: 30. The build currently referenced by CloudFront KVS (`meta.b`)
270
+ * is NEVER expired regardless of this value — only builds that have been
271
+ * replaced by a newer deploy are cleaned up (issue #480). Raise this to keep
272
+ * a longer rollback window. Must be at least `skewProtection.maxAge`
273
+ * (converted to days), or synth throws `InvalidSkewProtectionMaxAgeError`.
274
+ */
275
+ buildRetentionDays?: number;
276
+
211
277
  /** Custom Content-Security-Policy header value. */
212
278
  contentSecurityPolicy?: string;
213
279
 
@@ -399,7 +465,44 @@ export class Hosting extends Construct {
399
465
  /** SNS topic for hosting CloudWatch alarms (present when `monitoring.enabled` is true). */
400
466
  public readonly monitoringTopic?: cdk.aws_sns.ITopic;
401
467
 
402
- constructor(scope: Construct, id: string, props: HostingProps) {
468
+ /**
469
+ * Async constructor. Required when a `secret()` / `config()` value resolves at
470
+ * **synth time** — i.e. a `domain.domainName` marker — because that value is
471
+ * fetched from its store (Secrets Manager / SSM) during synthesis. Returns a
472
+ * fully-constructed {@link Hosting}.
473
+ *
474
+ * For runtime `environment` markers (read via `getSecret`/`getConfig`) and
475
+ * plain literals, `new Hosting(...)` works directly; `create()` is also safe
476
+ * to use uniformly.
477
+ *
478
+ * @example
479
+ * ```ts
480
+ * await Hosting.create(stack, 'Web', {
481
+ * root: '.', framework: 'nextjs', api: blocksStack,
482
+ * domain: { domainName: config('DOMAIN_PROD') },
483
+ * });
484
+ * ```
485
+ */
486
+ static async create(scope: Construct, id: string, props: HostingProps): Promise<Hosting> {
487
+ const storeConfig = { secretStore: props.secretStore, configStore: props.configStore };
488
+ const synthMarkers = collectSynthMarkers(props.domain?.domainName);
489
+ const resolved = synthMarkers.length
490
+ ? await resolveSecretsAtSynth(synthMarkers, undefined, storeConfig)
491
+ : new Map<string, string>();
492
+ // Fail the deploy early if a declared `environment` secret/config has no value
493
+ // set — existence only, never fetching the value. (Domain markers above already
494
+ // fail this way; this extends it to runtime markers.)
495
+ await assertMarkersExistAtSynth(partitionEnvironment(props.environment).managed, storeConfig);
496
+ return new Hosting(scope, id, props, resolved);
497
+ }
498
+
499
+ /**
500
+ * @param resolvedSecrets - Synth-time-resolved secret values, keyed by secret
501
+ * key. Populated by {@link Hosting.create}; empty for the direct
502
+ * `new Hosting()` path (which then rejects any synth-time secret with a
503
+ * clear "use Hosting.create()" error).
504
+ */
505
+ constructor(scope: Construct, id: string, props: HostingProps, resolvedSecrets: Map<string, string> = new Map()) {
403
506
  super(scope, id);
404
507
 
405
508
  const root = resolve(props.root);
@@ -415,7 +518,7 @@ export class Hosting extends Construct {
415
518
  // from breaking frontend builds (e.g., Next.js webpack module resolution).
416
519
  const nodeOptions = (process.env.NODE_OPTIONS || '')
417
520
  .split(/\s+/)
418
- .filter(opt => opt !== '--conditions=cdk')
521
+ .filter((opt) => opt !== '--conditions=cdk')
419
522
  .join(' ');
420
523
  const buildEnv = { ...process.env, NODE_OPTIONS: nodeOptions };
421
524
  try {
@@ -430,7 +533,7 @@ export class Hosting extends Construct {
430
533
  } catch (error) {
431
534
  throw new Error(
432
535
  `Frontend build failed: ${props.buildCommand}\n` +
433
- `Ensure the build command is correct and all dependencies are installed.`,
536
+ `Ensure the build command is correct and all dependencies are installed.`,
434
537
  );
435
538
  }
436
539
  }
@@ -445,7 +548,7 @@ export class Hosting extends Construct {
445
548
  if (!existsSync(expectedOutputDir)) {
446
549
  throw new Error(
447
550
  `Build output directory not found: ${expectedOutputDir}\n` +
448
- `Provide a buildCommand or ensure the directory exists before synthesis.`,
551
+ `Provide a buildCommand or ensure the directory exists before synthesis.`,
449
552
  );
450
553
  }
451
554
 
@@ -494,8 +597,7 @@ export class Hosting extends Construct {
494
597
  // errorPages will drive the CloudFront configuration correctly,
495
598
  // and the HTML files are already in the static assets directory
496
599
  // (copied by the adapter).
497
- const manifestHasErrorPages = manifest.errorPages !== undefined &&
498
- Object.keys(manifest.errorPages).length > 0;
600
+ const manifestHasErrorPages = manifest.errorPages !== undefined && Object.keys(manifest.errorPages).length > 0;
499
601
  const skipPropsErrorPages = !!(props.errorPages && manifestHasErrorPages);
500
602
 
501
603
  // ── 5. Write placeholder config.json into static assets ─────────
@@ -506,10 +608,7 @@ export class Hosting extends Construct {
506
608
  const staticDir = manifest.staticAssets.directory;
507
609
  const blocksSandboxDir = join(staticDir, '.blocks-sandbox');
508
610
  mkdirSync(blocksSandboxDir, { recursive: true });
509
- writeFileSync(
510
- join(blocksSandboxDir, 'config.json'),
511
- JSON.stringify({ _placeholder: true }),
512
- );
611
+ writeFileSync(join(blocksSandboxDir, 'config.json'), JSON.stringify({ _placeholder: true }));
513
612
 
514
613
  // ── 5a. Mark config.json as a no-cache path ─────────────────────
515
614
  // config.json is a fixed-name, mutable runtime-config file: it must
@@ -532,7 +631,7 @@ export class Hosting extends Construct {
532
631
  target: 'static',
533
632
  };
534
633
  // Insert before any catch-all route
535
- const catchAllIdx = manifest.routes.findIndex((r) => r.pattern === '/*' || r.pattern === '/(.*)')
634
+ const catchAllIdx = manifest.routes.findIndex((r) => r.pattern === '/*' || r.pattern === '/(.*)');
536
635
  if (catchAllIdx >= 0) {
537
636
  manifest.routes.splice(catchAllIdx, 0, blocksConfigRoute);
538
637
  } else {
@@ -550,28 +649,58 @@ export class Hosting extends Construct {
550
649
  const normalizedCompute = props.compute
551
650
  ? {
552
651
  ...props.compute,
553
- timeout: typeof props.compute.timeout === 'number'
554
- ? cdk.Duration.seconds(props.compute.timeout)
555
- : props.compute.timeout,
652
+ timeout:
653
+ typeof props.compute.timeout === 'number'
654
+ ? cdk.Duration.seconds(props.compute.timeout)
655
+ : props.compute.timeout,
656
+ }
657
+ : undefined;
658
+
659
+ // ── 6b. Resolve secret() markers ─────────────────────────────
660
+ // Env values split into plain / runtime-secret / deploy-time buckets;
661
+ // domain names may contain markers resolved at synth time. See
662
+ // ./hosting-secrets.ts for the two-strategy rationale.
663
+ const { plain: plainEnv, managed, byo } = partitionEnvironment(props.environment);
664
+
665
+ // Resolved domain (markers → literals); falls back to the raw config when
666
+ // no domain is set. resolveDomainNames throws a "use Hosting.create()"
667
+ // error if a domain marker reached the sync path unresolved.
668
+ const resolvedDomain = props.domain
669
+ ? {
670
+ ...props.domain,
671
+ domainName: resolveDomainNames(props.domain.domainName, resolvedSecrets),
556
672
  }
557
673
  : undefined;
558
674
 
559
675
  const hostingProps: HostingConstructProps = {
560
676
  manifest,
561
677
  compute: normalizedCompute,
562
- domain: props.domain,
678
+ // Plain env + deploy-time literals go straight to the L3 (which stays
679
+ // secret-agnostic: it only ever sees plain strings). Runtime secrets are
680
+ // wired separately below so we can attach IAM grants per function.
681
+ environment: Object.keys(plainEnv).length ? plainEnv : undefined,
682
+ domain: resolvedDomain,
563
683
  waf: props.waf,
564
- storage: props.retainOnDelete != null
565
- ? { retainOnDelete: props.retainOnDelete }
566
- : undefined,
567
- cdn: (props.contentSecurityPolicy || props.priceClass || props.geoRestriction || props.quotas)
568
- ? {
569
- contentSecurityPolicy: props.contentSecurityPolicy,
570
- priceClass: props.priceClass,
571
- geoRestriction: props.geoRestriction,
572
- quotas: props.quotas,
573
- }
574
- : undefined,
684
+ storage:
685
+ props.retainOnDelete != null || props.buildRetentionDays != null
686
+ ? {
687
+ ...(props.retainOnDelete != null
688
+ ? { retainOnDelete: props.retainOnDelete }
689
+ : {}),
690
+ ...(props.buildRetentionDays != null
691
+ ? { buildRetentionDays: props.buildRetentionDays }
692
+ : {}),
693
+ }
694
+ : undefined,
695
+ cdn:
696
+ props.contentSecurityPolicy || props.priceClass || props.geoRestriction || props.quotas
697
+ ? {
698
+ contentSecurityPolicy: props.contentSecurityPolicy,
699
+ priceClass: props.priceClass,
700
+ geoRestriction: props.geoRestriction,
701
+ quotas: props.quotas,
702
+ }
703
+ : undefined,
575
704
  logging: props.logging,
576
705
  buildCache: props.buildCache,
577
706
  errorPages: skipPropsErrorPages ? undefined : props.errorPages,
@@ -591,14 +720,10 @@ export class Hosting extends Construct {
591
720
  // variables — they surface in computeFunctions as EdgeFunction/IVersion
592
721
  // without an `addEnvironment` method. Skip any function that can't take
593
722
  // env vars instead of crashing (`fn.addEnvironment is not a function`).
594
- const canAddEnv = (
595
- fn: unknown,
596
- ): fn is cdk.aws_lambda.Function =>
723
+ const canAddEnv = (fn: unknown): fn is cdk.aws_lambda.Function =>
597
724
  typeof (fn as { addEnvironment?: unknown })?.addEnvironment === 'function';
598
725
 
599
- const primaryFunction = [...hosting.computeFunctions.values()].find(
600
- canAddEnv,
601
- );
726
+ const primaryFunction = [...hosting.computeFunctions.values()].find(canAddEnv);
602
727
 
603
728
  for (const [, fn] of hosting.computeFunctions) {
604
729
  if (!canAddEnv(fn)) continue; // Lambda@Edge: no env var support
@@ -608,15 +733,25 @@ export class Hosting extends Construct {
608
733
  if (props.backendConfig) {
609
734
  fn.addEnvironment('BLOCKS_CONFIG', JSON.stringify(props.backendConfig));
610
735
  }
736
+ // Runtime managed markers (secret|config): inject the store locator
737
+ // (never the value) + grant read/decrypt; read via getSecret()/getConfig().
738
+ // The app's secretStore/configStore (prefix, stage, cacheTtlSeconds) layer
739
+ // over the Blocks defaults.
740
+ const storeConfig = { secretStore: props.secretStore, configStore: props.configStore };
741
+ for (const marker of managed) {
742
+ wireManagedValue(fn, marker, storeConfig);
743
+ }
744
+ // BYO handles (ISecret|IParameter): grant via the handle + inject its locator.
745
+ for (const binding of byo) {
746
+ wireByo(fn, binding, storeConfig);
747
+ }
611
748
  }
612
749
 
613
750
  // ── 8. Deploy config.json with resolved CDK tokens ───────────
614
751
  const buildId = manifest.buildId;
615
752
  if (buildId) {
616
753
  const configDeployment = new s3deploy.BucketDeployment(this, 'BlocksConfigDeployment', {
617
- sources: [
618
- s3deploy.Source.jsonData('config.json', this.buildConfigJson(props)),
619
- ],
754
+ sources: [s3deploy.Source.jsonData('config.json', this.buildConfigJson(props))],
620
755
  destinationBucket: hosting.bucket,
621
756
  destinationKeyPrefix: `builds/${buildId}/.blocks-sandbox`,
622
757
  prune: false,
@@ -629,10 +764,7 @@ export class Hosting extends Construct {
629
764
  // primary guard against staleness is step 5a's no-cache placeholder;
630
765
  // this is defense-in-depth so a post-deploy invalidation actually
631
766
  // clears any edge entry at its real key.)
632
- distributionPaths: [
633
- `/builds/${buildId}/.blocks-sandbox/*`,
634
- '/.blocks-sandbox/*',
635
- ],
767
+ distributionPaths: [`/builds/${buildId}/.blocks-sandbox/*`, '/.blocks-sandbox/*'],
636
768
  cacheControl: [s3deploy.CacheControl.fromString('public, max-age=60, must-revalidate')],
637
769
  });
638
770
 
@@ -764,8 +896,8 @@ export class Hosting extends Construct {
764
896
  if (behaviorPattern.endsWith('/*')) {
765
897
  console.warn(
766
898
  `[Hosting] ⚠️ RawRoute '${route.path}' creates CloudFront behavior '${behaviorPattern}' ` +
767
- `which may shadow SSR/frontend routes under the same prefix. ` +
768
- `Consider placing this route under ${BLOCKS_RPC_PREFIX}/ to avoid conflicts.`,
899
+ `which may shadow SSR/frontend routes under the same prefix. ` +
900
+ `Consider placing this route under ${BLOCKS_RPC_PREFIX}/ to avoid conflicts.`,
769
901
  );
770
902
  }
771
903
 
package/src/index.cdk.ts CHANGED
@@ -1,50 +1,92 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- export { ApiNamespace, type BlocksContext, type ApiHandler } from './api.js';
5
- export { BLOCKS_RPC_PREFIX, BLOCKS_AUTH_PREFIX } from './constants.js';
6
- export { ApiError, isBlocksError, hasAuthError, DEFAULT_API_ERROR_NAME } from './errors.js';
7
- export { EventSourceMapping } from './lambda-handler.js';
8
- export { BlocksStackProps } from './common/index.js';
9
- export { registerSdkIdentifiers, getSdkIdentifiers, getAllSdkIdentifiers, _resetSdkRegistry } from './common/sdk-registry.js';
10
- export { getConfig, getConfigSync, preloadConfig, loadConfigToProcessEnv, _resetConfigCache } from './common/config.js';
11
- export { BlocksStack, Scope, SandboxDisableDeletionProtection, BlocksBackend, registerConfig, finalizeConfigRegistry, synthGuard, DEFAULT_NODE_RUNTIME, blocksNodejsBundling, type BlocksBackendProps } from './cdk/index.js';
4
+ // Declare functions for infra (secret/config). The runtime getters
5
+ // (getSecret/getConfig) are imported from '@aws-blocks/hosting' — core's own
6
+ // getConfig (backend config loader, above) owns that name here.
7
+ export {
8
+ type ConfigValue,
9
+ config,
10
+ DEFAULT_CONFIG_PARAMETER_PREFIX,
11
+ DEFAULT_SECRET_PARAMETER_PREFIX,
12
+ isConfig,
13
+ isManagedValue,
14
+ isSecret,
15
+ type ManagedValue,
16
+ type SecretValue,
17
+ secret,
18
+ type ValueKind,
19
+ } from '@aws-blocks/hosting';
20
+ export { type ApiHandler, ApiNamespace, type BlocksContext } from './api.js';
12
21
  export {
13
- type BlocksDefaults,
14
- BlocksPresets,
22
+ BlocksBackend,
23
+ type BlocksBackendProps,
24
+ type BlocksDefaults,
25
+ BlocksPresets,
26
+ BlocksStack,
27
+ type BlocksThrottling,
28
+ blocksNodejsBundling,
29
+ type CoreBlocksBackendProps,
30
+ type CoreBlocksStackProps,
31
+ DEFAULT_NODE_RUNTIME,
32
+ ensureApiGatewayAccount,
33
+ finalizeConfigRegistry,
34
+ getConfigLocation,
35
+ registerConfig,
36
+ SandboxDisableDeletionProtection,
37
+ Scope,
38
+ SHARED_HANDLER_TIMEOUT_SECONDS,
39
+ synthGuard,
15
40
  } from './cdk/index.js';
41
+ export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, preloadConfig } from './common/config.js';
42
+ export { BlocksStackProps } from './common/index.js';
43
+ export {
44
+ _resetSdkRegistry,
45
+ getAllSdkIdentifiers,
46
+ getSdkIdentifiers,
47
+ registerSdkIdentifiers,
48
+ } from './common/sdk-registry.js';
49
+ export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
50
+ export { ApiError, blocksError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
16
51
  export {
17
- Hosting,
18
- type HostingProps,
19
- type BlocksStackApi,
20
- type FrameworkType,
21
- type ComputeConfig,
22
- type HostingDomainConfig,
23
- type HostingWafConfig,
52
+ type BlocksStackApi,
53
+ type ComputeConfig,
54
+ type FrameworkType,
55
+ Hosting,
56
+ type HostingDomainConfig,
57
+ type HostingProps,
58
+ type HostingWafConfig,
24
59
  } from './hosting.js';
60
+ export { EventSourceMapping } from './lambda-handler.js';
61
+ export {
62
+ __PIPELINE_STAGE_SCOPE__,
63
+ type BranchConfig,
64
+ DeployStage,
65
+ type DeployStageProps,
66
+ Pipeline,
67
+ type PipelineProps,
68
+ type PipelineSourceConfig,
69
+ type PipelineStageConfig,
70
+ type PipelineSynthConfig,
71
+ } from './pipeline/index.js';
25
72
  export {
26
- RawRoute,
27
- RawRouteErrors,
28
- type RawRouteOptions,
29
- type HttpMethod,
73
+ type HttpMethod,
74
+ RawRoute,
75
+ RawRouteErrors,
76
+ type RawRouteOptions,
30
77
  } from './raw-route.cdk.js';
31
78
  export {
32
- registerRoute,
33
- matchRoute,
34
- getRegisteredRoutes,
35
- clearRouteRegistry,
36
- lockRouteRegistry,
37
- unlockRouteRegistry,
38
- type RegisteredRoute,
79
+ clearRouteRegistry,
80
+ getRegisteredRoutes,
81
+ lockRouteRegistry,
82
+ matchRoute,
83
+ type RegisteredRoute,
84
+ registerRoute,
85
+ unlockRouteRegistry,
39
86
  } from './raw-route.js';
40
87
  export {
41
- Pipeline,
42
- DeployStage,
43
- __PIPELINE_STAGE_SCOPE__,
44
- type DeployStageProps,
45
- type BranchConfig,
46
- type PipelineProps,
47
- type PipelineSourceConfig,
48
- type PipelineSynthConfig,
49
- type PipelineStageConfig,
50
- } from './pipeline/index.js';
88
+ BLOCKS_CONFIG_PARAMETER_PREFIX,
89
+ BLOCKS_SECRET_PARAMETER_PREFIX,
90
+ blocksConfigParameterName,
91
+ blocksSecretParameterName,
92
+ } from './secret-naming.js';
package/src/index.ts CHANGED
@@ -1,22 +1,41 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- export { ApiNamespace, type BlocksContext, type ApiHandler } from './api.js';
5
- export { BLOCKS_RPC_PREFIX, BLOCKS_AUTH_PREFIX } from './constants.js';
6
- export { ApiError, isBlocksError, hasAuthError, DEFAULT_API_ERROR_NAME } from './errors.js';
7
- export { Scope, type ScopeOptions, type ScopeParent, type BuildingBlockMeta } from './common/index.js';
8
- export { registerSdkIdentifiers, getSdkIdentifiers, getAllSdkIdentifiers, _resetSdkRegistry } from './common/sdk-registry.js';
9
- export { getConfig, getConfigSync, preloadConfig, loadConfigToProcessEnv, _resetConfigCache } from './common/config.js';
4
+ // secret()/config() declare markers (CDK-free subpath). The runtime getters
5
+ // getSecret()/getConfig() are imported from '@aws-blocks/hosting' directly —
6
+ // core already exports a backend `getConfig` (config loader) under this name.
10
7
  export {
11
- registerRoute,
12
- matchRoute,
13
- getRegisteredRoutes,
14
- clearRouteRegistry,
15
- lockRouteRegistry,
16
- unlockRouteRegistry,
17
- RawRouteErrors,
18
- type RawRouteOptions,
19
- type HttpMethod,
20
- type RegisteredRoute,
8
+ type ConfigValue,
9
+ config,
10
+ isConfig,
11
+ isManagedValue,
12
+ isSecret,
13
+ type ManagedValue,
14
+ type SecretValue,
15
+ secret,
16
+ type ValueKind,
17
+ } from '@aws-blocks/hosting';
18
+ export { type ApiHandler, ApiNamespace, type BlocksContext } from './api.js';
19
+ export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, preloadConfig } from './common/config.js';
20
+ export { type BuildingBlockMeta, Scope, type ScopeOptions, type ScopeParent } from './common/index.js';
21
+ export {
22
+ _resetSdkRegistry,
23
+ getAllSdkIdentifiers,
24
+ getSdkIdentifiers,
25
+ registerSdkIdentifiers,
26
+ } from './common/sdk-registry.js';
27
+ export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
28
+ export { ApiError, blocksError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
29
+ export {
30
+ clearRouteRegistry,
31
+ getRegisteredRoutes,
32
+ type HttpMethod,
33
+ lockRouteRegistry,
34
+ matchRoute,
35
+ RawRouteErrors,
36
+ type RawRouteOptions,
37
+ type RegisteredRoute,
38
+ registerRoute,
39
+ unlockRouteRegistry,
21
40
  } from './raw-route.js';
22
41
  export { RawRoute } from './raw-route.mock.js';