@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/dist/errors.d.ts CHANGED
@@ -70,6 +70,22 @@ export declare class ApiError extends Error {
70
70
  export declare function isBlocksError<N extends string>(e: unknown, name: N): e is Error & {
71
71
  name: N;
72
72
  };
73
+ /**
74
+ * Build a named `Error` whose `name` is a BB error constant, so it is matchable
75
+ * with {@link isBlocksError} on both server and client. The name is also
76
+ * prefixed into the message for readable logs.
77
+ *
78
+ * This is the producer half of the {@link isBlocksError} contract: throw via
79
+ * this helper so the `name` a consumer matches on is set consistently. It has
80
+ * no runtime dependencies, so it is safe to use in every bundle — mock,
81
+ * aws-runtime, and CDK synth.
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * throw blocksError(KVStoreErrors.ConditionalCheckFailed, 'Key already exists');
86
+ * ```
87
+ */
88
+ export declare function blocksError(name: string, message: string): Error;
73
89
  /**
74
90
  * Type guard for branching on a failed `AuthState` (the recommended
75
91
  * `setAuthState` client path) by its structured `errorName`.
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,aAAa,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAClC,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE;CAM9G;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAE7F;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,SAAS,MAAM,EAC9E,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EAC3B,IAAI,EAAE,CAAC,GACL,KAAK,IAAI,CAAC,GAAG;IAAE,SAAS,EAAE,CAAC,CAAA;CAAE,CAE/B"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,aAAa,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAClC,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE;CAM9G;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,CAE7F;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAIhE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,SAAS,MAAM,EAC9E,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EAC3B,IAAI,EAAE,CAAC,GACL,KAAK,IAAI,CAAC,GAAG;IAAE,SAAS,EAAE,CAAC,CAAA;CAAE,CAE/B"}
package/dist/errors.js CHANGED
@@ -73,6 +73,26 @@ export class ApiError extends Error {
73
73
  export function isBlocksError(e, name) {
74
74
  return e instanceof Error && e.name === name;
75
75
  }
76
+ /**
77
+ * Build a named `Error` whose `name` is a BB error constant, so it is matchable
78
+ * with {@link isBlocksError} on both server and client. The name is also
79
+ * prefixed into the message for readable logs.
80
+ *
81
+ * This is the producer half of the {@link isBlocksError} contract: throw via
82
+ * this helper so the `name` a consumer matches on is set consistently. It has
83
+ * no runtime dependencies, so it is safe to use in every bundle — mock,
84
+ * aws-runtime, and CDK synth.
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * throw blocksError(KVStoreErrors.ConditionalCheckFailed, 'Key already exists');
89
+ * ```
90
+ */
91
+ export function blocksError(name, message) {
92
+ const err = new Error(`${name}: ${message}`);
93
+ err.name = name;
94
+ return err;
95
+ }
76
96
  /**
77
97
  * Type guard for branching on a failed `AuthState` (the recommended
78
98
  * `setAuthState` client path) by its structured `errorName`.
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Resolution glue between the dependency-free `secret()` / `config()` markers
3
+ * (defined in `@aws-blocks/hosting`) and the `Hosting` CDK block.
4
+ *
5
+ * Delegates to the shared `@aws-blocks/hosting` engine, pinning only the Blocks
6
+ * namespaces (`/blocks/secrets`, `/blocks/config`). The store is derived from the
7
+ * marker's kind (`secret` → Secrets Manager, `config` → SSM), and BYO
8
+ * `ISecret`/`IParameter` handles are wired identically.
9
+ *
10
+ * @module
11
+ */
12
+ import type { ManagedValue } from '@aws-blocks/hosting';
13
+ import { type ByoBinding, type DomainNameInput as LeafDomainNameInput, type EnvValue as LeafEnvValue, type SecretFetcher, type StoreConfig } from '@aws-blocks/hosting/constructs';
14
+ import type * as cdk from 'aws-cdk-lib';
15
+ /** A `compute.environment` value: a literal, a managed marker, or a BYO CDK handle. */
16
+ export type EnvValue = LeafEnvValue;
17
+ /** A custom-domain name: a literal, a managed marker, or a mix in an array. */
18
+ export type DomainNameInput = LeafDomainNameInput;
19
+ /** Split an environment map into plain literals, managed markers, and BYO handles. */
20
+ export declare function partitionEnvironment(environment: Record<string, EnvValue> | undefined): {
21
+ plain: Record<string, string>;
22
+ managed: ManagedValue[];
23
+ byo: ByoBinding[];
24
+ };
25
+ /** Every managed marker requiring a synth-time fetch (domain markers only). */
26
+ export declare function collectSynthMarkers(domainName: DomainNameInput | undefined): ManagedValue[];
27
+ /**
28
+ * Resolve managed markers to plaintext at synth time under the Blocks namespaces.
29
+ * Throws a clear, actionable error if a referenced value was never set. An optional
30
+ * `storeConfig` overrides the pinned Blocks prefix/stage per kind.
31
+ */
32
+ export declare function resolveSecretsAtSynth(markers: ManagedValue[], fetcher?: SecretFetcher, storeConfig?: StoreConfig): Promise<Map<string, string>>;
33
+ /** Resolve domain markers to literals using the synth-resolved value map. */
34
+ export declare function resolveDomainNames(domainName: DomainNameInput, resolved: Map<string, string>): string | string[];
35
+ /**
36
+ * Fail synth (deploy) when a referenced `environment` marker has no value set —
37
+ * existence only, never fetching the value. Uses the Blocks namespaces (with an
38
+ * optional per-kind `storeConfig` override), matching how the values are wired.
39
+ */
40
+ export declare function assertMarkersExistAtSynth(markers: ManagedValue[], storeConfig?: StoreConfig): Promise<void>;
41
+ /**
42
+ * Wire a runtime managed marker under the Blocks namespace (store from kind). An
43
+ * optional `storeConfig` overrides the pinned prefix/stage/cacheTtlSeconds per kind.
44
+ */
45
+ export declare function wireManagedValue(fn: cdk.aws_lambda.Function, marker: ManagedValue, storeConfig?: StoreConfig): void;
46
+ /** Wire a runtime BYO handle (grant + inject locator). Honors an optional `storeConfig`. */
47
+ export declare function wireByo(fn: cdk.aws_lambda.Function, binding: ByoBinding, storeConfig?: StoreConfig): void;
48
+ //# sourceMappingURL=hosting-secrets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosting-secrets.d.ts","sourceRoot":"","sources":["../src/hosting-secrets.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EACN,KAAK,UAAU,EACf,KAAK,eAAe,IAAI,mBAAmB,EAC3C,KAAK,QAAQ,IAAI,YAAY,EAM7B,KAAK,aAAa,EAClB,KAAK,WAAW,EAGhB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,KAAK,GAAG,MAAM,aAAa,CAAC;AAiBxC,uFAAuF;AACvF,MAAM,MAAM,QAAQ,GAAG,YAAY,CAAC;AAEpC,+EAA+E;AAC/E,MAAM,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAElD,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,SAAS,GAAG;IACxF,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,GAAG,EAAE,UAAU,EAAE,CAAC;CAClB,CAEA;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,eAAe,GAAG,SAAS,GAAG,YAAY,EAAE,CAE3F;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAC1C,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,CAAC,EAAE,aAAa,EACvB,WAAW,CAAC,EAAE,WAAW,GACvB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAE9B;AAED,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,eAAe,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,EAAE,CAEhH;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjH;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAEnH;AAED,4FAA4F;AAC5F,wBAAgB,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAEzG"}
@@ -0,0 +1,57 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { assertMarkersExistAtSynth as leafAssertMarkersExistAtSynth, collectSynthMarkers as leafCollectSynthMarkers, partitionEnvironment as leafPartitionEnvironment, resolveDomainNames as leafResolveDomainNames, resolveSecretsAtSynth as leafResolveSecretsAtSynth, wireByo as leafWireByo, wireManagedValue as leafWireManagedValue, } from '@aws-blocks/hosting/constructs';
4
+ import { blocksStoreConfig } from './secret-naming.js';
5
+ /**
6
+ * Layer a caller's per-kind store config over the pinned Blocks defaults. Shallow
7
+ * and per-kind: the Blocks `/blocks/*` prefix stays the default, but any field the
8
+ * app sets (`prefix`, `stage`, `cacheTtlSeconds`) wins.
9
+ */
10
+ function mergeBlocksStoreConfig(override) {
11
+ const base = blocksStoreConfig();
12
+ if (!override)
13
+ return base;
14
+ return {
15
+ secretStore: { ...base.secretStore, ...override.secretStore },
16
+ configStore: { ...base.configStore, ...override.configStore },
17
+ };
18
+ }
19
+ /** Split an environment map into plain literals, managed markers, and BYO handles. */
20
+ export function partitionEnvironment(environment) {
21
+ return leafPartitionEnvironment(environment);
22
+ }
23
+ /** Every managed marker requiring a synth-time fetch (domain markers only). */
24
+ export function collectSynthMarkers(domainName) {
25
+ return leafCollectSynthMarkers(domainName);
26
+ }
27
+ /**
28
+ * Resolve managed markers to plaintext at synth time under the Blocks namespaces.
29
+ * Throws a clear, actionable error if a referenced value was never set. An optional
30
+ * `storeConfig` overrides the pinned Blocks prefix/stage per kind.
31
+ */
32
+ export async function resolveSecretsAtSynth(markers, fetcher, storeConfig) {
33
+ return leafResolveSecretsAtSynth(markers, { ...mergeBlocksStoreConfig(storeConfig), fetcher });
34
+ }
35
+ /** Resolve domain markers to literals using the synth-resolved value map. */
36
+ export function resolveDomainNames(domainName, resolved) {
37
+ return leafResolveDomainNames(domainName, resolved);
38
+ }
39
+ /**
40
+ * Fail synth (deploy) when a referenced `environment` marker has no value set —
41
+ * existence only, never fetching the value. Uses the Blocks namespaces (with an
42
+ * optional per-kind `storeConfig` override), matching how the values are wired.
43
+ */
44
+ export async function assertMarkersExistAtSynth(markers, storeConfig) {
45
+ return leafAssertMarkersExistAtSynth(markers, mergeBlocksStoreConfig(storeConfig));
46
+ }
47
+ /**
48
+ * Wire a runtime managed marker under the Blocks namespace (store from kind). An
49
+ * optional `storeConfig` overrides the pinned prefix/stage/cacheTtlSeconds per kind.
50
+ */
51
+ export function wireManagedValue(fn, marker, storeConfig) {
52
+ leafWireManagedValue(fn, marker, mergeBlocksStoreConfig(storeConfig));
53
+ }
54
+ /** Wire a runtime BYO handle (grant + inject locator). Honors an optional `storeConfig`. */
55
+ export function wireByo(fn, binding, storeConfig) {
56
+ leafWireByo(fn, binding, mergeBlocksStoreConfig(storeConfig));
57
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hosting-secrets.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosting-secrets.test.d.ts","sourceRoot":"","sources":["../src/hosting-secrets.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,81 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import assert from 'node:assert';
4
+ import { describe, it } from 'node:test';
5
+ import { config, secret } from '@aws-blocks/hosting';
6
+ import { collectSynthMarkers, partitionEnvironment, resolveDomainNames, resolveSecretsAtSynth, } from './hosting-secrets.js';
7
+ void describe('partitionEnvironment()', () => {
8
+ void it('splits plain / managed (secret+config)', () => {
9
+ const { plain, managed } = partitionEnvironment({
10
+ FLAG: 'on',
11
+ STRIPE_KEY: secret('STRIPE_KEY'),
12
+ FEATURE_FLAGS: config('FEATURE_FLAGS'),
13
+ });
14
+ assert.deepStrictEqual(plain, { FLAG: 'on' });
15
+ assert.deepStrictEqual(managed.map((m) => `${m.key}:${m.kind}`).sort(), [
16
+ 'FEATURE_FLAGS:config',
17
+ 'STRIPE_KEY:secret',
18
+ ]);
19
+ });
20
+ void it('rejects env key / marker key mismatch', () => {
21
+ assert.throws(() => partitionEnvironment({ STRIPE_KEY: secret('OTHER') }), /must match the environment variable name/);
22
+ });
23
+ void it('handles undefined', () => {
24
+ const { plain, managed, byo } = partitionEnvironment(undefined);
25
+ assert.deepStrictEqual(plain, {});
26
+ assert.strictEqual(managed.length, 0);
27
+ assert.strictEqual(byo.length, 0);
28
+ });
29
+ });
30
+ void describe('collectSynthMarkers()', () => {
31
+ void it('gathers domain markers, deduped by key', () => {
32
+ const markers = collectSynthMarkers(['example.com', config('DOMAIN_PROD'), config('DOMAIN_PROD')]);
33
+ assert.deepStrictEqual(markers.map((m) => m.key), ['DOMAIN_PROD']);
34
+ });
35
+ void it('returns nothing when no markers', () => {
36
+ assert.deepStrictEqual(collectSynthMarkers('example.com'), []);
37
+ assert.deepStrictEqual(collectSynthMarkers(undefined), []);
38
+ });
39
+ });
40
+ void describe('resolveSecretsAtSynth() — Blocks namespaces', () => {
41
+ void it('config marker resolves under /blocks/config (SSM leading-slash)', async () => {
42
+ const seen = [];
43
+ const resolved = await resolveSecretsAtSynth([config('DOMAIN_PROD')], async (name) => {
44
+ seen.push(name);
45
+ return 'prod.example.com';
46
+ });
47
+ assert.deepStrictEqual(seen, ['/blocks/config/DOMAIN_PROD']);
48
+ assert.strictEqual(resolved.get('DOMAIN_PROD'), 'prod.example.com');
49
+ });
50
+ void it('secret marker resolves under /blocks/secrets (Secrets Manager slash-free)', async () => {
51
+ const seen = [];
52
+ await resolveSecretsAtSynth([secret('API_KEY')], async (name) => {
53
+ seen.push(name);
54
+ return 'k';
55
+ });
56
+ assert.deepStrictEqual(seen, ['blocks/secrets/API_KEY']);
57
+ });
58
+ void it('throws an actionable error when a value is not set', async () => {
59
+ await assert.rejects(resolveSecretsAtSynth([config('DOMAIN_PROD')], async () => {
60
+ const e = new Error('not found');
61
+ e.name = 'ParameterNotFound';
62
+ throw e;
63
+ }), /config 'DOMAIN_PROD' is referenced/);
64
+ });
65
+ });
66
+ void describe('resolveDomainNames()', () => {
67
+ void it('replaces markers with resolved literals, preserving shape', () => {
68
+ const resolved = new Map([['DOMAIN_PROD', 'prod.example.com']]);
69
+ assert.strictEqual(resolveDomainNames(config('DOMAIN_PROD'), resolved), 'prod.example.com');
70
+ assert.deepStrictEqual(resolveDomainNames(['www.example.com', config('DOMAIN_PROD')], resolved), [
71
+ 'www.example.com',
72
+ 'prod.example.com',
73
+ ]);
74
+ });
75
+ void it('throws if a marker reached the sync path unresolved', () => {
76
+ assert.throws(() => resolveDomainNames(config('DOMAIN_PROD'), new Map()), /requires async resolution/);
77
+ });
78
+ void it('passes literal domains through untouched', () => {
79
+ assert.strictEqual(resolveDomainNames('example.com', new Map()), 'example.com');
80
+ });
81
+ });
package/dist/hosting.d.ts CHANGED
@@ -1,8 +1,9 @@
1
+ import type { FrameworkType, KindStoreOptions } from '@aws-blocks/hosting/constructs';
2
+ import { type FrameworkAdapterFn } from '@aws-blocks/hosting/adapters';
3
+ import { type HostingDomainConfig, type HostingWafConfig, type SkewProtectionConfig } from '@aws-blocks/hosting/constructs';
1
4
  import * as cdk from 'aws-cdk-lib';
2
5
  import { Construct } from 'constructs';
3
- import { type HostingDomainConfig, type HostingWafConfig, type SkewProtectionConfig } from '@aws-blocks/hosting/constructs';
4
- import { type FrameworkAdapterFn } from '@aws-blocks/hosting/adapters';
5
- import type { FrameworkType } from '@aws-blocks/hosting';
6
+ import { type DomainNameInput, type EnvValue } from './hosting-secrets.js';
6
7
  /**
7
8
  * Lambda compute configuration for SSR frameworks.
8
9
  *
@@ -62,6 +63,18 @@ export type ComputeConfig = {
62
63
  logRetention?: cdk.aws_logs.RetentionDays;
63
64
  };
64
65
  export type { FrameworkType, HostingDomainConfig, HostingWafConfig, SkewProtectionConfig };
66
+ /**
67
+ * Custom-domain config for the Blocks {@link Hosting} block — the leaf
68
+ * {@link HostingDomainConfig} with `domainName` widened to also accept a
69
+ * `config()` marker (resolved to a literal at synth). Only `config()` or a plain
70
+ * string is accepted here, never `secret()` (a synth-inlined value would leak into
71
+ * the template). All other fields (`hostedZone`, `certificate`, `wwwRedirect`, …)
72
+ * are inherited unchanged from {@link HostingDomainConfig}.
73
+ */
74
+ export interface HostingDomain extends Omit<HostingDomainConfig, 'domainName'> {
75
+ /** Domain name(s): a plain string / string[] and/or a `config()` marker resolved at synth. */
76
+ domainName: DomainNameInput;
77
+ }
65
78
  /**
66
79
  * Structural interface for the Blocks backend stack.
67
80
  *
@@ -146,12 +159,65 @@ export interface HostingProps {
146
159
  backendConfig?: Record<string, unknown>;
147
160
  /** Lambda compute configuration for SSR frameworks. */
148
161
  compute?: ComputeConfig;
149
- /** Custom domain configuration. */
150
- domain?: HostingDomainConfig;
162
+ /**
163
+ * Custom environment variables injected into all compute Lambda functions.
164
+ *
165
+ * Values are either plain strings or {@link secret} references:
166
+ *
167
+ * ```ts
168
+ * environment: {
169
+ * FEATURE_FLAG: 'on', // plaintext (in CFN template + Lambda config)
170
+ * STRIPE_KEY: secret('STRIPE_KEY'), // encrypted; read at runtime via getSecret()
171
+ * }
172
+ * ```
173
+ *
174
+ * A plain string is injected verbatim (visible in the CloudFormation
175
+ * template — safe for non-sensitive literals). A `secret('K')` value (AWS
176
+ * Secrets Manager) or `config('K')` value (SSM Parameter Store) injects only
177
+ * the store LOCATOR — never the value — and grants the compute role scoped
178
+ * read + decrypt; fetch it at runtime with `await getSecret('K')` /
179
+ * `await getConfig('K')`. A bring-your-own CDK `ISecret` / `IParameter` handle
180
+ * is accepted too and resolves identically.
181
+ *
182
+ * The env-var name must equal the key: `STRIPE_KEY: secret('STRIPE_KEY')`.
183
+ */
184
+ environment?: Record<string, EnvValue>;
185
+ /**
186
+ * Custom domain configuration. `domainName` accepts plain strings and/or
187
+ * {@link config} references (e.g. `domain: { domainName: config('DOMAIN_PROD') }`).
188
+ * A domain is resolved at synth time and inlined as a literal into the template,
189
+ * so only `config()` (non-sensitive → SSM) is allowed here, never `secret()`
190
+ * (which would leak into the template). Any domain marker requires constructing
191
+ * via the async {@link Hosting.create}.
192
+ */
193
+ domain?: HostingDomain;
194
+ /**
195
+ * Namespace/cache config for `secret()` values (AWS Secrets Manager). Defaults
196
+ * to the Blocks `/blocks/secrets` prefix; set `prefix` to namespace per app,
197
+ * `stage` for per-stage values with a shared fallback, or `cacheTtlSeconds` to
198
+ * refresh a rotated value on a warm compute without a cold start. Any field the
199
+ * app sets overrides the Blocks default.
200
+ */
201
+ secretStore?: KindStoreOptions;
202
+ /**
203
+ * Namespace/cache config for `config()` values (SSM Parameter Store). Defaults
204
+ * to the Blocks `/blocks/config` prefix; same override semantics as
205
+ * {@link HostingProps.secretStore}.
206
+ */
207
+ configStore?: KindStoreOptions;
151
208
  /** WAF protection configuration. */
152
209
  waf?: HostingWafConfig;
153
210
  /** Retain the S3 bucket when the stack is deleted. Default: false. */
154
211
  retainOnDelete?: boolean;
212
+ /**
213
+ * Days to retain SUPERSEDED build artifacts in S3 before they are expired.
214
+ * Default: 30. The build currently referenced by CloudFront KVS (`meta.b`)
215
+ * is NEVER expired regardless of this value — only builds that have been
216
+ * replaced by a newer deploy are cleaned up (issue #480). Raise this to keep
217
+ * a longer rollback window. Must be at least `skewProtection.maxAge`
218
+ * (converted to days), or synth throws `InvalidSkewProtectionMaxAgeError`.
219
+ */
220
+ buildRetentionDays?: number;
155
221
  /** Custom Content-Security-Policy header value. */
156
222
  contentSecurityPolicy?: string;
157
223
  /** CloudFront price class. Default: PRICE_CLASS_100. */
@@ -322,7 +388,32 @@ export declare class Hosting extends Construct {
322
388
  readonly buildCacheBucket?: cdk.aws_s3.Bucket;
323
389
  /** SNS topic for hosting CloudWatch alarms (present when `monitoring.enabled` is true). */
324
390
  readonly monitoringTopic?: cdk.aws_sns.ITopic;
325
- constructor(scope: Construct, id: string, props: HostingProps);
391
+ /**
392
+ * Async constructor. Required when a `secret()` / `config()` value resolves at
393
+ * **synth time** — i.e. a `domain.domainName` marker — because that value is
394
+ * fetched from its store (Secrets Manager / SSM) during synthesis. Returns a
395
+ * fully-constructed {@link Hosting}.
396
+ *
397
+ * For runtime `environment` markers (read via `getSecret`/`getConfig`) and
398
+ * plain literals, `new Hosting(...)` works directly; `create()` is also safe
399
+ * to use uniformly.
400
+ *
401
+ * @example
402
+ * ```ts
403
+ * await Hosting.create(stack, 'Web', {
404
+ * root: '.', framework: 'nextjs', api: blocksStack,
405
+ * domain: { domainName: config('DOMAIN_PROD') },
406
+ * });
407
+ * ```
408
+ */
409
+ static create(scope: Construct, id: string, props: HostingProps): Promise<Hosting>;
410
+ /**
411
+ * @param resolvedSecrets - Synth-time-resolved secret values, keyed by secret
412
+ * key. Populated by {@link Hosting.create}; empty for the direct
413
+ * `new Hosting()` path (which then rejects any synth-time secret with a
414
+ * clear "use Hosting.create()" error).
415
+ */
416
+ constructor(scope: Construct, id: string, props: HostingProps, resolvedSecrets?: Map<string, string>);
326
417
  /**
327
418
  * Build the config.json payload from props.
328
419
  *
@@ -1 +1 @@
1
- {"version":3,"file":"hosting.d.ts","sourceRoot":"","sources":["../src/hosting.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hosting.d.ts","sourceRoot":"","sources":["../src/hosting.ts"],"names":[],"mappings":"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"}
package/dist/hosting.js CHANGED
@@ -1,18 +1,19 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
+ import { execSync } from 'node:child_process';
4
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
5
+ import { join, resolve } from 'node:path';
6
+ import { detectFramework, getAdapter, normalizeBasePath } from '@aws-blocks/hosting/adapters';
7
+ import { generateBuildId, HostingConstruct, } from '@aws-blocks/hosting/constructs';
3
8
  import * as cdk from 'aws-cdk-lib';
4
- import { AllowedMethods, CachePolicy, OriginRequestPolicy, ViewerProtocolPolicy, } from 'aws-cdk-lib/aws-cloudfront';
9
+ import { AllowedMethods, CachePolicy, OriginRequestPolicy, ViewerProtocolPolicy } from 'aws-cdk-lib/aws-cloudfront';
5
10
  import { HttpOrigin } from 'aws-cdk-lib/aws-cloudfront-origins';
6
11
  import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment';
7
12
  import { Construct } from 'constructs';
8
- import { execSync } from 'node:child_process';
9
- import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
10
- import { join, resolve } from 'node:path';
11
- import { HostingConstruct, generateBuildId, } from '@aws-blocks/hosting/constructs';
12
- import { detectFramework, getAdapter, normalizeBasePath, } from '@aws-blocks/hosting/adapters';
13
- import { BLOCKS_RPC_PREFIX, BLOCKS_AUTH_PREFIX } from './constants.js';
14
- import { BLOCKS_SANDBOX_DIR } from './common/constants.js';
15
13
  import { registerConfig } from './cdk/config-registry.js';
14
+ import { BLOCKS_SANDBOX_DIR } from './common/constants.js';
15
+ import { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
16
+ import { assertMarkersExistAtSynth, collectSynthMarkers, partitionEnvironment, resolveDomainNames, resolveSecretsAtSynth, wireByo, wireManagedValue, } from './hosting-secrets.js';
16
17
  import { getRegisteredRoutes } from './raw-route.js';
17
18
  // ─── Default build output directories per framework ──────────────
18
19
  const DEFAULT_BUILD_DIRS = {
@@ -71,7 +72,43 @@ export class Hosting extends Construct {
71
72
  buildCacheBucket;
72
73
  /** SNS topic for hosting CloudWatch alarms (present when `monitoring.enabled` is true). */
73
74
  monitoringTopic;
74
- constructor(scope, id, props) {
75
+ /**
76
+ * Async constructor. Required when a `secret()` / `config()` value resolves at
77
+ * **synth time** — i.e. a `domain.domainName` marker — because that value is
78
+ * fetched from its store (Secrets Manager / SSM) during synthesis. Returns a
79
+ * fully-constructed {@link Hosting}.
80
+ *
81
+ * For runtime `environment` markers (read via `getSecret`/`getConfig`) and
82
+ * plain literals, `new Hosting(...)` works directly; `create()` is also safe
83
+ * to use uniformly.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * await Hosting.create(stack, 'Web', {
88
+ * root: '.', framework: 'nextjs', api: blocksStack,
89
+ * domain: { domainName: config('DOMAIN_PROD') },
90
+ * });
91
+ * ```
92
+ */
93
+ static async create(scope, id, props) {
94
+ const storeConfig = { secretStore: props.secretStore, configStore: props.configStore };
95
+ const synthMarkers = collectSynthMarkers(props.domain?.domainName);
96
+ const resolved = synthMarkers.length
97
+ ? await resolveSecretsAtSynth(synthMarkers, undefined, storeConfig)
98
+ : new Map();
99
+ // Fail the deploy early if a declared `environment` secret/config has no value
100
+ // set — existence only, never fetching the value. (Domain markers above already
101
+ // fail this way; this extends it to runtime markers.)
102
+ await assertMarkersExistAtSynth(partitionEnvironment(props.environment).managed, storeConfig);
103
+ return new Hosting(scope, id, props, resolved);
104
+ }
105
+ /**
106
+ * @param resolvedSecrets - Synth-time-resolved secret values, keyed by secret
107
+ * key. Populated by {@link Hosting.create}; empty for the direct
108
+ * `new Hosting()` path (which then rejects any synth-time secret with a
109
+ * clear "use Hosting.create()" error).
110
+ */
111
+ constructor(scope, id, props, resolvedSecrets = new Map()) {
75
112
  super(scope, id);
76
113
  const root = resolve(props.root);
77
114
  // ── 1. Detect framework ──────────────────────────────────────
@@ -84,7 +121,7 @@ export class Hosting extends Construct {
84
121
  // from breaking frontend builds (e.g., Next.js webpack module resolution).
85
122
  const nodeOptions = (process.env.NODE_OPTIONS || '')
86
123
  .split(/\s+/)
87
- .filter(opt => opt !== '--conditions=cdk')
124
+ .filter((opt) => opt !== '--conditions=cdk')
88
125
  .join(' ');
89
126
  const buildEnv = { ...process.env, NODE_OPTIONS: nodeOptions };
90
127
  try {
@@ -154,8 +191,7 @@ export class Hosting extends Construct {
154
191
  // errorPages will drive the CloudFront configuration correctly,
155
192
  // and the HTML files are already in the static assets directory
156
193
  // (copied by the adapter).
157
- const manifestHasErrorPages = manifest.errorPages !== undefined &&
158
- Object.keys(manifest.errorPages).length > 0;
194
+ const manifestHasErrorPages = manifest.errorPages !== undefined && Object.keys(manifest.errorPages).length > 0;
159
195
  const skipPropsErrorPages = !!(props.errorPages && manifestHasErrorPages);
160
196
  // ── 5. Write placeholder config.json into static assets ─────────
161
197
  // A placeholder is needed so the L3 construct sees
@@ -208,15 +244,40 @@ export class Hosting extends Construct {
208
244
  : props.compute.timeout,
209
245
  }
210
246
  : undefined;
247
+ // ── 6b. Resolve secret() markers ─────────────────────────────
248
+ // Env values split into plain / runtime-secret / deploy-time buckets;
249
+ // domain names may contain markers resolved at synth time. See
250
+ // ./hosting-secrets.ts for the two-strategy rationale.
251
+ const { plain: plainEnv, managed, byo } = partitionEnvironment(props.environment);
252
+ // Resolved domain (markers → literals); falls back to the raw config when
253
+ // no domain is set. resolveDomainNames throws a "use Hosting.create()"
254
+ // error if a domain marker reached the sync path unresolved.
255
+ const resolvedDomain = props.domain
256
+ ? {
257
+ ...props.domain,
258
+ domainName: resolveDomainNames(props.domain.domainName, resolvedSecrets),
259
+ }
260
+ : undefined;
211
261
  const hostingProps = {
212
262
  manifest,
213
263
  compute: normalizedCompute,
214
- domain: props.domain,
264
+ // Plain env + deploy-time literals go straight to the L3 (which stays
265
+ // secret-agnostic: it only ever sees plain strings). Runtime secrets are
266
+ // wired separately below so we can attach IAM grants per function.
267
+ environment: Object.keys(plainEnv).length ? plainEnv : undefined,
268
+ domain: resolvedDomain,
215
269
  waf: props.waf,
216
- storage: props.retainOnDelete != null
217
- ? { retainOnDelete: props.retainOnDelete }
270
+ storage: props.retainOnDelete != null || props.buildRetentionDays != null
271
+ ? {
272
+ ...(props.retainOnDelete != null
273
+ ? { retainOnDelete: props.retainOnDelete }
274
+ : {}),
275
+ ...(props.buildRetentionDays != null
276
+ ? { buildRetentionDays: props.buildRetentionDays }
277
+ : {}),
278
+ }
218
279
  : undefined,
219
- cdn: (props.contentSecurityPolicy || props.priceClass || props.geoRestriction || props.quotas)
280
+ cdn: props.contentSecurityPolicy || props.priceClass || props.geoRestriction || props.quotas
220
281
  ? {
221
282
  contentSecurityPolicy: props.contentSecurityPolicy,
222
283
  priceClass: props.priceClass,
@@ -251,14 +312,24 @@ export class Hosting extends Construct {
251
312
  if (props.backendConfig) {
252
313
  fn.addEnvironment('BLOCKS_CONFIG', JSON.stringify(props.backendConfig));
253
314
  }
315
+ // Runtime managed markers (secret|config): inject the store locator
316
+ // (never the value) + grant read/decrypt; read via getSecret()/getConfig().
317
+ // The app's secretStore/configStore (prefix, stage, cacheTtlSeconds) layer
318
+ // over the Blocks defaults.
319
+ const storeConfig = { secretStore: props.secretStore, configStore: props.configStore };
320
+ for (const marker of managed) {
321
+ wireManagedValue(fn, marker, storeConfig);
322
+ }
323
+ // BYO handles (ISecret|IParameter): grant via the handle + inject its locator.
324
+ for (const binding of byo) {
325
+ wireByo(fn, binding, storeConfig);
326
+ }
254
327
  }
255
328
  // ── 8. Deploy config.json with resolved CDK tokens ───────────
256
329
  const buildId = manifest.buildId;
257
330
  if (buildId) {
258
331
  const configDeployment = new s3deploy.BucketDeployment(this, 'BlocksConfigDeployment', {
259
- sources: [
260
- s3deploy.Source.jsonData('config.json', this.buildConfigJson(props)),
261
- ],
332
+ sources: [s3deploy.Source.jsonData('config.json', this.buildConfigJson(props))],
262
333
  destinationBucket: hosting.bucket,
263
334
  destinationKeyPrefix: `builds/${buildId}/.blocks-sandbox`,
264
335
  prune: false,
@@ -271,10 +342,7 @@ export class Hosting extends Construct {
271
342
  // primary guard against staleness is step 5a's no-cache placeholder;
272
343
  // this is defense-in-depth so a post-deploy invalidation actually
273
344
  // clears any edge entry at its real key.)
274
- distributionPaths: [
275
- `/builds/${buildId}/.blocks-sandbox/*`,
276
- '/.blocks-sandbox/*',
277
- ],
345
+ distributionPaths: [`/builds/${buildId}/.blocks-sandbox/*`, '/.blocks-sandbox/*'],
278
346
  cacheControl: [s3deploy.CacheControl.fromString('public, max-age=60, must-revalidate')],
279
347
  });
280
348
  // Ensure the config deployment runs AFTER the hosting construct's