@aws-blocks/core 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/cdk/blocks-backend.d.ts +35 -9
  2. package/dist/cdk/blocks-backend.d.ts.map +1 -1
  3. package/dist/cdk/blocks-backend.js +52 -69
  4. package/dist/cdk/blocks-backend.test.js +61 -69
  5. package/dist/cdk/blocks-defaults.d.ts +27 -1
  6. package/dist/cdk/blocks-defaults.d.ts.map +1 -1
  7. package/dist/cdk/blocks-defaults.js +5 -1
  8. package/dist/cdk/blocks-defaults.test.js +4 -2
  9. package/dist/cdk/blocks-stack.test.js +73 -37
  10. package/dist/cdk/compute/default-compute-factory.d.ts +33 -0
  11. package/dist/cdk/compute/default-compute-factory.d.ts.map +1 -0
  12. package/dist/cdk/compute/default-compute-factory.js +3 -0
  13. package/dist/cdk/index.d.ts +46 -5
  14. package/dist/cdk/index.d.ts.map +1 -1
  15. package/dist/cdk/index.js +57 -7
  16. package/dist/cdk/internal.d.ts +1 -0
  17. package/dist/cdk/internal.d.ts.map +1 -1
  18. package/dist/hosting-secrets.d.ts +48 -0
  19. package/dist/hosting-secrets.d.ts.map +1 -0
  20. package/dist/hosting-secrets.js +57 -0
  21. package/dist/hosting-secrets.test.d.ts +2 -0
  22. package/dist/hosting-secrets.test.d.ts.map +1 -0
  23. package/dist/hosting-secrets.test.js +81 -0
  24. package/dist/hosting.d.ts +88 -6
  25. package/dist/hosting.d.ts.map +1 -1
  26. package/dist/hosting.js +83 -24
  27. package/dist/hosting.test.js +158 -10
  28. package/dist/index.cdk.d.ts +13 -12
  29. package/dist/index.cdk.d.ts.map +1 -1
  30. package/dist/index.cdk.js +13 -9
  31. package/dist/index.d.ts +8 -7
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +9 -5
  34. package/dist/lambda-handler.test.js +66 -0
  35. package/dist/pipeline/index.d.ts +24 -2
  36. package/dist/pipeline/index.d.ts.map +1 -1
  37. package/dist/pipeline/index.js +41 -1
  38. package/dist/pipeline.test.d.ts +2 -0
  39. package/dist/pipeline.test.d.ts.map +1 -0
  40. package/dist/pipeline.test.js +47 -0
  41. package/dist/rpc.d.ts +21 -0
  42. package/dist/rpc.d.ts.map +1 -1
  43. package/dist/rpc.js +34 -0
  44. package/dist/rpc.test.js +38 -1
  45. package/dist/scripts/config.d.ts +15 -0
  46. package/dist/scripts/config.d.ts.map +1 -0
  47. package/dist/scripts/config.js +29 -0
  48. package/dist/scripts/deploy.d.ts.map +1 -1
  49. package/dist/scripts/deploy.js +4 -0
  50. package/dist/scripts/dev-server.d.ts +8 -0
  51. package/dist/scripts/dev-server.d.ts.map +1 -1
  52. package/dist/scripts/dev-server.js +31 -0
  53. package/dist/scripts/generate-client.d.ts.map +1 -1
  54. package/dist/scripts/generate-client.js +9 -4
  55. package/dist/scripts/generate-client.test.js +19 -4
  56. package/dist/scripts/index.d.ts +13 -10
  57. package/dist/scripts/index.d.ts.map +1 -1
  58. package/dist/scripts/index.js +11 -8
  59. package/dist/scripts/preflight-credentials.d.ts +30 -0
  60. package/dist/scripts/preflight-credentials.d.ts.map +1 -0
  61. package/dist/scripts/preflight-credentials.js +108 -0
  62. package/dist/scripts/preflight-credentials.test.d.ts +2 -0
  63. package/dist/scripts/preflight-credentials.test.d.ts.map +1 -0
  64. package/dist/scripts/preflight-credentials.test.js +77 -0
  65. package/dist/scripts/sandbox-args.test.d.ts +2 -0
  66. package/dist/scripts/sandbox-args.test.d.ts.map +1 -0
  67. package/dist/scripts/sandbox-args.test.js +77 -0
  68. package/dist/scripts/sandbox.d.ts +52 -0
  69. package/dist/scripts/sandbox.d.ts.map +1 -1
  70. package/dist/scripts/sandbox.js +92 -15
  71. package/dist/scripts/secret.d.ts +15 -0
  72. package/dist/scripts/secret.d.ts.map +1 -0
  73. package/dist/scripts/secret.js +29 -0
  74. package/dist/scripts/secret.test.d.ts +2 -0
  75. package/dist/scripts/secret.test.d.ts.map +1 -0
  76. package/dist/scripts/secret.test.js +28 -0
  77. package/dist/scripts/typegen.d.ts +15 -0
  78. package/dist/scripts/typegen.d.ts.map +1 -0
  79. package/dist/scripts/typegen.js +16 -0
  80. package/dist/secret-naming.d.ts +58 -0
  81. package/dist/secret-naming.d.ts.map +1 -0
  82. package/dist/secret-naming.js +67 -0
  83. package/dist/secret-naming.test.d.ts +2 -0
  84. package/dist/secret-naming.test.d.ts.map +1 -0
  85. package/dist/secret-naming.test.js +40 -0
  86. package/dist/version.d.ts +1 -1
  87. package/dist/version.js +1 -1
  88. package/package.json +2 -1
  89. package/src/cdk/blocks-backend.test.ts +267 -272
  90. package/src/cdk/blocks-backend.ts +71 -79
  91. package/src/cdk/blocks-defaults.test.ts +4 -2
  92. package/src/cdk/blocks-defaults.ts +27 -1
  93. package/src/cdk/blocks-stack.test.ts +161 -118
  94. package/src/cdk/compute/default-compute-factory.ts +37 -0
  95. package/src/cdk/index.ts +84 -8
  96. package/src/cdk/internal.ts +1 -0
  97. package/src/hosting-secrets.test.ts +106 -0
  98. package/src/hosting-secrets.ts +106 -0
  99. package/src/hosting.test.ts +244 -63
  100. package/src/hosting.ts +180 -68
  101. package/src/index.cdk.ts +77 -38
  102. package/src/index.ts +35 -16
  103. package/src/lambda-handler.test.ts +74 -0
  104. package/src/pipeline/index.ts +64 -11
  105. package/src/pipeline.test.ts +55 -0
  106. package/src/rpc.test.ts +44 -1
  107. package/src/rpc.ts +41 -0
  108. package/src/scripts/config.ts +35 -0
  109. package/src/scripts/deploy.ts +5 -0
  110. package/src/scripts/dev-server.ts +40 -0
  111. package/src/scripts/generate-client.test.ts +43 -8
  112. package/src/scripts/generate-client.ts +70 -65
  113. package/src/scripts/index.ts +20 -17
  114. package/src/scripts/preflight-credentials.test.ts +110 -0
  115. package/src/scripts/preflight-credentials.ts +131 -0
  116. package/src/scripts/sandbox-args.test.ts +92 -0
  117. package/src/scripts/sandbox.ts +102 -15
  118. package/src/scripts/secret.test.ts +35 -0
  119. package/src/scripts/secret.ts +35 -0
  120. package/src/scripts/typegen.ts +18 -0
  121. package/src/secret-naming.test.ts +59 -0
  122. package/src/secret-naming.ts +99 -0
  123. package/src/version.ts +1 -1
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,8 +159,52 @@ 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. */
@@ -322,7 +379,32 @@ export declare class Hosting extends Construct {
322
379
  readonly buildCacheBucket?: cdk.aws_s3.Bucket;
323
380
  /** SNS topic for hosting CloudWatch alarms (present when `monitoring.enabled` is true). */
324
381
  readonly monitoringTopic?: cdk.aws_sns.ITopic;
325
- constructor(scope: Construct, id: string, props: HostingProps);
382
+ /**
383
+ * Async constructor. Required when a `secret()` / `config()` value resolves at
384
+ * **synth time** — i.e. a `domain.domainName` marker — because that value is
385
+ * fetched from its store (Secrets Manager / SSM) during synthesis. Returns a
386
+ * fully-constructed {@link Hosting}.
387
+ *
388
+ * For runtime `environment` markers (read via `getSecret`/`getConfig`) and
389
+ * plain literals, `new Hosting(...)` works directly; `create()` is also safe
390
+ * to use uniformly.
391
+ *
392
+ * @example
393
+ * ```ts
394
+ * await Hosting.create(stack, 'Web', {
395
+ * root: '.', framework: 'nextjs', api: blocksStack,
396
+ * domain: { domainName: config('DOMAIN_PROD') },
397
+ * });
398
+ * ```
399
+ */
400
+ static create(scope: Construct, id: string, props: HostingProps): Promise<Hosting>;
401
+ /**
402
+ * @param resolvedSecrets - Synth-time-resolved secret values, keyed by secret
403
+ * key. Populated by {@link Hosting.create}; empty for the direct
404
+ * `new Hosting()` path (which then rejects any synth-time secret with a
405
+ * clear "use Hosting.create()" error).
406
+ */
407
+ constructor(scope: Construct, id: string, props: HostingProps, resolvedSecrets?: Map<string, string>);
326
408
  /**
327
409
  * Build the config.json payload from props.
328
410
  *
@@ -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,31 @@ 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 }
218
- : undefined,
219
- cdn: (props.contentSecurityPolicy || props.priceClass || props.geoRestriction || props.quotas)
270
+ storage: props.retainOnDelete != null ? { retainOnDelete: props.retainOnDelete } : undefined,
271
+ cdn: props.contentSecurityPolicy || props.priceClass || props.geoRestriction || props.quotas
220
272
  ? {
221
273
  contentSecurityPolicy: props.contentSecurityPolicy,
222
274
  priceClass: props.priceClass,
@@ -251,14 +303,24 @@ export class Hosting extends Construct {
251
303
  if (props.backendConfig) {
252
304
  fn.addEnvironment('BLOCKS_CONFIG', JSON.stringify(props.backendConfig));
253
305
  }
306
+ // Runtime managed markers (secret|config): inject the store locator
307
+ // (never the value) + grant read/decrypt; read via getSecret()/getConfig().
308
+ // The app's secretStore/configStore (prefix, stage, cacheTtlSeconds) layer
309
+ // over the Blocks defaults.
310
+ const storeConfig = { secretStore: props.secretStore, configStore: props.configStore };
311
+ for (const marker of managed) {
312
+ wireManagedValue(fn, marker, storeConfig);
313
+ }
314
+ // BYO handles (ISecret|IParameter): grant via the handle + inject its locator.
315
+ for (const binding of byo) {
316
+ wireByo(fn, binding, storeConfig);
317
+ }
254
318
  }
255
319
  // ── 8. Deploy config.json with resolved CDK tokens ───────────
256
320
  const buildId = manifest.buildId;
257
321
  if (buildId) {
258
322
  const configDeployment = new s3deploy.BucketDeployment(this, 'BlocksConfigDeployment', {
259
- sources: [
260
- s3deploy.Source.jsonData('config.json', this.buildConfigJson(props)),
261
- ],
323
+ sources: [s3deploy.Source.jsonData('config.json', this.buildConfigJson(props))],
262
324
  destinationBucket: hosting.bucket,
263
325
  destinationKeyPrefix: `builds/${buildId}/.blocks-sandbox`,
264
326
  prune: false,
@@ -271,10 +333,7 @@ export class Hosting extends Construct {
271
333
  // primary guard against staleness is step 5a's no-cache placeholder;
272
334
  // this is defense-in-depth so a post-deploy invalidation actually
273
335
  // clears any edge entry at its real key.)
274
- distributionPaths: [
275
- `/builds/${buildId}/.blocks-sandbox/*`,
276
- '/.blocks-sandbox/*',
277
- ],
336
+ distributionPaths: [`/builds/${buildId}/.blocks-sandbox/*`, '/.blocks-sandbox/*'],
278
337
  cacheControl: [s3deploy.CacheControl.fromString('public, max-age=60, must-revalidate')],
279
338
  });
280
339
  // Ensure the config deployment runs AFTER the hosting construct's
@@ -1,13 +1,15 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { describe, it, beforeEach, afterEach } from 'node:test';
4
3
  import assert from 'node:assert';
5
4
  import * as fs from 'node:fs';
6
- import * as path from 'node:path';
7
5
  import * as os from 'node:os';
6
+ import * as path from 'node:path';
7
+ import { afterEach, beforeEach, describe, it } from 'node:test';
8
+ import { config, secret } from '@aws-blocks/hosting';
9
+ import { _setSynthExistsChecker } from '@aws-blocks/hosting/constructs';
8
10
  import * as cdk from 'aws-cdk-lib';
9
11
  import { App, Duration, Stack } from 'aws-cdk-lib';
10
- import { Template, Match } from 'aws-cdk-lib/assertions';
12
+ import { Match, Template } from 'aws-cdk-lib/assertions';
11
13
  import { BLOCKS_RPC_PREFIX } from './constants.js';
12
14
  import { Hosting } from './hosting.js';
13
15
  import { clearRouteRegistry, registerRoute } from './raw-route.js';
@@ -84,6 +86,31 @@ describe('Hosting', () => {
84
86
  fs.rmSync(tmpDir, { recursive: true, force: true });
85
87
  clearRouteRegistry();
86
88
  });
89
+ // ── Deploy-time existence check (key change 4) ───────────────
90
+ describe('environment marker existence check (Hosting.create)', () => {
91
+ it('rejects at deploy when a declared environment secret has no value', async () => {
92
+ createSpaBuildOutput(tmpDir);
93
+ const app = new App();
94
+ const stack = new Stack(app, 'MissingEnvSecretStack');
95
+ // Stub the synth existence probe to report "not set" — rejects before any
96
+ // CDK construction, so this needs no assets and no AWS.
97
+ _setSynthExistsChecker(async () => {
98
+ const e = new Error('missing');
99
+ e.name = 'ResourceNotFoundException';
100
+ throw e;
101
+ });
102
+ try {
103
+ await assert.rejects(Hosting.create(stack, 'Hosting', {
104
+ root: tmpDir,
105
+ api: MOCK_API,
106
+ environment: { MISSING_SECRET: secret('MISSING_SECRET') },
107
+ }), /MISSING_SECRET.*not set/s);
108
+ }
109
+ finally {
110
+ _setSynthExistsChecker(null);
111
+ }
112
+ });
113
+ });
87
114
  // ── SPA tests ────────────────────────────────────────────────
88
115
  describe('SPA mode', () => {
89
116
  it('creates S3 bucket and CloudFront distribution', () => {
@@ -1092,8 +1119,7 @@ describe('Hosting', () => {
1092
1119
  ...(basePath !== undefined ? { basePath } : {}),
1093
1120
  });
1094
1121
  const tpl = Template.fromStack(stack).toJSON();
1095
- const kvKeys = Object.entries(tpl.Resources).find(([id, r]) => r.Type === 'AWS::CloudFormation::CustomResource' &&
1096
- /RouteStoreKeys/.test(id));
1122
+ const kvKeys = Object.entries(tpl.Resources).find(([id, r]) => r.Type === 'AWS::CloudFormation::CustomResource' && /RouteStoreKeys/.test(id));
1097
1123
  assert.ok(kvKeys, 'expected a RouteStoreKeys custom resource');
1098
1124
  const entries = JSON.parse(kvKeys[1].Properties.Entries);
1099
1125
  const meta = JSON.parse(entries.meta);
@@ -1134,6 +1160,131 @@ describe('Hosting', () => {
1134
1160
  `found DependsOn=${JSON.stringify(dependsOn)}`);
1135
1161
  });
1136
1162
  });
1163
+ // ── value() integration ─────────────────────────────────────────
1164
+ //
1165
+ // Runtime values: the store LOCATOR (never the value) is injected as a Lambda
1166
+ // env var, and the role is granted read + kms:Decrypt. The store is derived from
1167
+ // the marker's type — secret → Secrets Manager, config → SSM Parameter Store.
1168
+ // Domain markers resolve at synth time via Hosting.create().
1169
+ describe('secret() / config() integration', () => {
1170
+ it('secret() → Secrets Manager: injects the locator (not the value) + grants read+decrypt', () => {
1171
+ createNextjsBuildOutput(tmpDir);
1172
+ const app = new App();
1173
+ const stack = new Stack(app, 'SecretEnvStack');
1174
+ new Hosting(stack, 'Hosting', {
1175
+ root: tmpDir,
1176
+ customAdapter: createNextjsFixtureAdapter(tmpDir),
1177
+ api: MOCK_API,
1178
+ environment: { STRIPE_KEY: secret('STRIPE_KEY') },
1179
+ });
1180
+ const template = Template.fromStack(stack);
1181
+ // secret → Secrets Manager: slash-free locator under the Blocks /blocks/secrets namespace.
1182
+ template.hasResourceProperties('AWS::Lambda::Function', {
1183
+ Environment: {
1184
+ Variables: Match.objectLike({ HOSTING_SECRET_PARAM_STRIPE_KEY: 'blocks/secrets/STRIPE_KEY' }),
1185
+ },
1186
+ });
1187
+ // The plaintext value must NOT appear anywhere in the template.
1188
+ const json = JSON.stringify(template.toJSON());
1189
+ assert.ok(!json.includes('STRIPE_KEY_VALUE'), 'no value should be in the template');
1190
+ // The role has secretsmanager:GetSecretValue scoped to the secret + kms:Decrypt.
1191
+ template.hasResourceProperties('AWS::IAM::Policy', {
1192
+ PolicyDocument: {
1193
+ Statement: Match.arrayWith([
1194
+ Match.objectLike({ Action: 'secretsmanager:GetSecretValue' }),
1195
+ Match.objectLike({
1196
+ Action: 'kms:Decrypt',
1197
+ Condition: { StringEquals: Match.objectLike({ 'kms:ViaService': Match.anyValue() }) },
1198
+ }),
1199
+ ]),
1200
+ },
1201
+ });
1202
+ });
1203
+ it('threads secretStore { prefix, stage, cacheTtlSeconds } from core HostingProps to the wiring (B3)', () => {
1204
+ createNextjsBuildOutput(tmpDir);
1205
+ const app = new App();
1206
+ const stack = new Stack(app, 'SecretStoreOverrideStack');
1207
+ new Hosting(stack, 'Hosting', {
1208
+ root: tmpDir,
1209
+ customAdapter: createNextjsFixtureAdapter(tmpDir),
1210
+ api: MOCK_API,
1211
+ environment: { STRIPE_KEY: secret('STRIPE_KEY') },
1212
+ // Custom prefix + stage + warm-cache TTL must all reach the leaf wiring.
1213
+ secretStore: { prefix: '/myapp/secrets', stage: 'prod', cacheTtlSeconds: 30 },
1214
+ });
1215
+ const template = Template.fromStack(stack);
1216
+ template.hasResourceProperties('AWS::Lambda::Function', {
1217
+ Environment: {
1218
+ Variables: Match.objectLike({
1219
+ // custom prefix + stage (Secrets Manager → slash-free)
1220
+ HOSTING_SECRET_PARAM_STRIPE_KEY: 'myapp/secrets/prod/STRIPE_KEY',
1221
+ // shared fallback (no stage)
1222
+ HOSTING_SECRET_PARAM_STRIPE_KEY_FALLBACK: 'myapp/secrets/STRIPE_KEY',
1223
+ // warm-cache TTL for rotation without a cold start
1224
+ HOSTING_SECRET_CACHE_TTL: '30',
1225
+ }),
1226
+ },
1227
+ });
1228
+ });
1229
+ it('config() → SSM: HOSTING_CONFIG_PARAM_* (leading-slash) + ssm:GetParameter', () => {
1230
+ createNextjsBuildOutput(tmpDir);
1231
+ const app = new App();
1232
+ const stack = new Stack(app, 'ConfigEnvStack');
1233
+ new Hosting(stack, 'Hosting', {
1234
+ root: tmpDir,
1235
+ customAdapter: createNextjsFixtureAdapter(tmpDir),
1236
+ api: MOCK_API,
1237
+ environment: { FEATURE_FLAGS: config('FEATURE_FLAGS') },
1238
+ });
1239
+ const template = Template.fromStack(stack);
1240
+ template.hasResourceProperties('AWS::Lambda::Function', {
1241
+ Environment: {
1242
+ Variables: Match.objectLike({ HOSTING_CONFIG_PARAM_FEATURE_FLAGS: '/blocks/config/FEATURE_FLAGS' }),
1243
+ },
1244
+ });
1245
+ template.hasResourceProperties('AWS::IAM::Policy', {
1246
+ PolicyDocument: { Statement: Match.arrayWith([Match.objectLike({ Action: 'ssm:GetParameter' })]) },
1247
+ });
1248
+ });
1249
+ it('does not inject a plaintext env var for a runtime value', () => {
1250
+ createNextjsBuildOutput(tmpDir);
1251
+ const app = new App();
1252
+ const stack = new Stack(app, 'SecretNoPlaintextStack');
1253
+ new Hosting(stack, 'Hosting', {
1254
+ root: tmpDir,
1255
+ customAdapter: createNextjsFixtureAdapter(tmpDir),
1256
+ api: MOCK_API,
1257
+ environment: { FLAG: 'on', STRIPE_KEY: secret('STRIPE_KEY') },
1258
+ });
1259
+ const template = Template.fromStack(stack);
1260
+ const fns = template.findResources('AWS::Lambda::Function');
1261
+ for (const fn of Object.values(fns)) {
1262
+ const vars = (fn.Properties?.Environment?.Variables ?? {});
1263
+ assert.strictEqual(vars.STRIPE_KEY, undefined, 'value key must not be a plaintext env var');
1264
+ }
1265
+ });
1266
+ it('rejects a runtime value whose key mismatches the env var name', () => {
1267
+ createNextjsBuildOutput(tmpDir);
1268
+ const app = new App();
1269
+ const stack = new Stack(app, 'SecretMismatchStack');
1270
+ assert.throws(() => new Hosting(stack, 'Hosting', {
1271
+ root: tmpDir,
1272
+ customAdapter: createNextjsFixtureAdapter(tmpDir),
1273
+ api: MOCK_API,
1274
+ environment: { STRIPE_KEY: secret('OTHER') },
1275
+ }), /must match the environment variable name/);
1276
+ });
1277
+ it('throws if a domain marker is used with the sync constructor', () => {
1278
+ createSpaBuildOutput(tmpDir);
1279
+ const app = new App();
1280
+ const stack = new Stack(app, 'SecretDomainSyncStack');
1281
+ assert.throws(() => new Hosting(stack, 'Hosting', {
1282
+ root: tmpDir,
1283
+ api: MOCK_API,
1284
+ domain: { domainName: config('DOMAIN_PROD') },
1285
+ }), /requires async resolution/);
1286
+ });
1287
+ });
1137
1288
  describe('config.json stale-placeholder guard (#173)', () => {
1138
1289
  // Helper: pull every BucketDeployment custom resource's Properties.
1139
1290
  const bucketDeployments = (stack) => {
@@ -1169,8 +1320,7 @@ describe('Hosting', () => {
1169
1320
  directive.includes('s-maxage=31536000') &&
1170
1321
  !excludes(p).includes('.blocks-sandbox/config.json'));
1171
1322
  });
1172
- assert.strictEqual(leaks.length, 0, 'no mutable-tier deployment may apply s-maxage=31536000 to ' +
1173
- '.blocks-sandbox/config.json');
1323
+ assert.strictEqual(leaks.length, 0, 'no mutable-tier deployment may apply s-maxage=31536000 to ' + '.blocks-sandbox/config.json');
1174
1324
  });
1175
1325
  it('registers the placeholder as a no-cache path even for a static-only site (no api)', () => {
1176
1326
  // The placeholder is always written (step 5), so its no-cache
@@ -1201,9 +1351,7 @@ describe('Hosting', () => {
1201
1351
  // Assert via Template + Match so a CDK property rename fails loudly here
1202
1352
  // rather than silently skipping a structural-heuristic lookup.
1203
1353
  Template.fromStack(stack).hasResourceProperties('Custom::CDKBucketDeployment', Match.objectLike({
1204
- DistributionPaths: Match.arrayWith([
1205
- Match.stringLikeRegexp('^/builds/.+/\\.blocks-sandbox/\\*$'),
1206
- ]),
1354
+ DistributionPaths: Match.arrayWith([Match.stringLikeRegexp('^/builds/.+/\\.blocks-sandbox/\\*$')]),
1207
1355
  }));
1208
1356
  });
1209
1357
  });
@@ -1,14 +1,15 @@
1
- export { ApiNamespace, type BlocksContext, type ApiHandler } from './api.js';
2
- export { BLOCKS_RPC_PREFIX, BLOCKS_AUTH_PREFIX } from './constants.js';
3
- export { ApiError, isBlocksError, hasAuthError, DEFAULT_API_ERROR_NAME } from './errors.js';
4
- export { EventSourceMapping } from './lambda-handler.js';
1
+ export { type ConfigValue, config, DEFAULT_CONFIG_PARAMETER_PREFIX, DEFAULT_SECRET_PARAMETER_PREFIX, isConfig, isManagedValue, isSecret, type ManagedValue, type SecretValue, secret, type ValueKind, } from '@aws-blocks/hosting';
2
+ export { type ApiHandler, ApiNamespace, type BlocksContext } from './api.js';
3
+ export { BlocksBackend, type BlocksBackendProps, type BlocksDefaults, BlocksPresets, BlocksStack, blocksNodejsBundling, type CoreBlocksBackendProps, type CoreBlocksStackProps, DEFAULT_NODE_RUNTIME, finalizeConfigRegistry, registerConfig, SandboxDisableDeletionProtection, Scope, SHARED_HANDLER_TIMEOUT_SECONDS, synthGuard, } from './cdk/index.js';
4
+ export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, preloadConfig } from './common/config.js';
5
5
  export { BlocksStackProps } from './common/index.js';
6
- export { registerSdkIdentifiers, getSdkIdentifiers, getAllSdkIdentifiers, _resetSdkRegistry } from './common/sdk-registry.js';
7
- export { getConfig, getConfigSync, preloadConfig, loadConfigToProcessEnv, _resetConfigCache } from './common/config.js';
8
- export { BlocksStack, Scope, SandboxDisableDeletionProtection, BlocksBackend, registerConfig, finalizeConfigRegistry, synthGuard, DEFAULT_NODE_RUNTIME, blocksNodejsBundling, type BlocksBackendProps } from './cdk/index.js';
9
- export { type BlocksDefaults, BlocksPresets, } from './cdk/index.js';
10
- export { Hosting, type HostingProps, type BlocksStackApi, type FrameworkType, type ComputeConfig, type HostingDomainConfig, type HostingWafConfig, } from './hosting.js';
11
- export { RawRoute, RawRouteErrors, type RawRouteOptions, type HttpMethod, } from './raw-route.cdk.js';
12
- export { registerRoute, matchRoute, getRegisteredRoutes, clearRouteRegistry, lockRouteRegistry, unlockRouteRegistry, type RegisteredRoute, } from './raw-route.js';
13
- export { Pipeline, DeployStage, __PIPELINE_STAGE_SCOPE__, type DeployStageProps, type BranchConfig, type PipelineProps, type PipelineSourceConfig, type PipelineSynthConfig, type PipelineStageConfig, } from './pipeline/index.js';
6
+ export { _resetSdkRegistry, getAllSdkIdentifiers, getSdkIdentifiers, registerSdkIdentifiers, } from './common/sdk-registry.js';
7
+ export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
8
+ export { ApiError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
9
+ export { type BlocksStackApi, type ComputeConfig, type FrameworkType, Hosting, type HostingDomainConfig, type HostingProps, type HostingWafConfig, } from './hosting.js';
10
+ export { EventSourceMapping } from './lambda-handler.js';
11
+ export { __PIPELINE_STAGE_SCOPE__, type BranchConfig, DeployStage, type DeployStageProps, Pipeline, type PipelineProps, type PipelineSourceConfig, type PipelineStageConfig, type PipelineSynthConfig, } from './pipeline/index.js';
12
+ export { type HttpMethod, RawRoute, RawRouteErrors, type RawRouteOptions, } from './raw-route.cdk.js';
13
+ export { clearRouteRegistry, getRegisteredRoutes, lockRouteRegistry, matchRoute, type RegisteredRoute, registerRoute, unlockRouteRegistry, } from './raw-route.js';
14
+ export { BLOCKS_CONFIG_PARAMETER_PREFIX, BLOCKS_SECRET_PARAMETER_PREFIX, blocksConfigParameterName, blocksSecretParameterName, } from './secret-naming.js';
14
15
  //# sourceMappingURL=index.cdk.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC9H,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACxH,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,gCAAgC,EAAE,aAAa,EAAE,cAAc,EAAE,sBAAsB,EAAE,UAAU,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC9N,OAAO,EACL,KAAK,cAAc,EACnB,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,OAAO,EACP,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,QAAQ,EACR,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,UAAU,GAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,aAAa,EACb,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,QAAQ,EACR,WAAW,EACX,wBAAwB,EACxB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,KAAK,WAAW,EAChB,MAAM,EACN,+BAA+B,EAC/B,+BAA+B,EAC/B,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,MAAM,EACN,KAAK,SAAS,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EACN,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,aAAa,EACb,WAAW,EACX,oBAAoB,EACpB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,oBAAoB,EACpB,sBAAsB,EACtB,cAAc,EACd,gCAAgC,EAChC,KAAK,EACL,8BAA8B,EAC9B,UAAU,GACV,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,aAAa,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxH,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EACN,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,GACtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,OAAO,EACP,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,gBAAgB,GACrB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EACN,wBAAwB,EACxB,KAAK,YAAY,EACjB,WAAW,EACX,KAAK,gBAAgB,EACrB,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,KAAK,UAAU,EACf,QAAQ,EACR,cAAc,EACd,KAAK,eAAe,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,EACV,KAAK,eAAe,EACpB,aAAa,EACb,mBAAmB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACN,8BAA8B,EAC9B,8BAA8B,EAC9B,yBAAyB,EACzB,yBAAyB,GACzB,MAAM,oBAAoB,CAAC"}
package/dist/index.cdk.js CHANGED
@@ -1,14 +1,18 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
+ // Declare functions for infra (secret/config). The runtime getters
4
+ // (getSecret/getConfig) are imported from '@aws-blocks/hosting' — core's own
5
+ // getConfig (backend config loader, above) owns that name here.
6
+ export { config, DEFAULT_CONFIG_PARAMETER_PREFIX, DEFAULT_SECRET_PARAMETER_PREFIX, isConfig, isManagedValue, isSecret, secret, } from '@aws-blocks/hosting';
3
7
  export { ApiNamespace } from './api.js';
4
- export { BLOCKS_RPC_PREFIX, BLOCKS_AUTH_PREFIX } from './constants.js';
5
- export { ApiError, isBlocksError, hasAuthError, DEFAULT_API_ERROR_NAME } from './errors.js';
6
- export { EventSourceMapping } from './lambda-handler.js';
7
- export { registerSdkIdentifiers, getSdkIdentifiers, getAllSdkIdentifiers, _resetSdkRegistry } from './common/sdk-registry.js';
8
- export { getConfig, getConfigSync, preloadConfig, loadConfigToProcessEnv, _resetConfigCache } from './common/config.js';
9
- export { BlocksStack, Scope, SandboxDisableDeletionProtection, BlocksBackend, registerConfig, finalizeConfigRegistry, synthGuard, DEFAULT_NODE_RUNTIME, blocksNodejsBundling } from './cdk/index.js';
10
- export { BlocksPresets, } from './cdk/index.js';
8
+ export { BlocksBackend, BlocksPresets, BlocksStack, blocksNodejsBundling, DEFAULT_NODE_RUNTIME, finalizeConfigRegistry, registerConfig, SandboxDisableDeletionProtection, Scope, SHARED_HANDLER_TIMEOUT_SECONDS, synthGuard, } from './cdk/index.js';
9
+ export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, preloadConfig } from './common/config.js';
10
+ export { _resetSdkRegistry, getAllSdkIdentifiers, getSdkIdentifiers, registerSdkIdentifiers, } from './common/sdk-registry.js';
11
+ export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
12
+ export { ApiError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
11
13
  export { Hosting, } from './hosting.js';
14
+ export { EventSourceMapping } from './lambda-handler.js';
15
+ export { __PIPELINE_STAGE_SCOPE__, DeployStage, Pipeline, } from './pipeline/index.js';
12
16
  export { RawRoute, RawRouteErrors, } from './raw-route.cdk.js';
13
- export { registerRoute, matchRoute, getRegisteredRoutes, clearRouteRegistry, lockRouteRegistry, unlockRouteRegistry, } from './raw-route.js';
14
- export { Pipeline, DeployStage, __PIPELINE_STAGE_SCOPE__, } from './pipeline/index.js';
17
+ export { clearRouteRegistry, getRegisteredRoutes, lockRouteRegistry, matchRoute, registerRoute, unlockRouteRegistry, } from './raw-route.js';
18
+ export { BLOCKS_CONFIG_PARAMETER_PREFIX, BLOCKS_SECRET_PARAMETER_PREFIX, blocksConfigParameterName, blocksSecretParameterName, } from './secret-naming.js';
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- export { ApiNamespace, type BlocksContext, type ApiHandler } from './api.js';
2
- export { BLOCKS_RPC_PREFIX, BLOCKS_AUTH_PREFIX } from './constants.js';
3
- export { ApiError, isBlocksError, hasAuthError, DEFAULT_API_ERROR_NAME } from './errors.js';
4
- export { Scope, type ScopeOptions, type ScopeParent, type BuildingBlockMeta } from './common/index.js';
5
- export { registerSdkIdentifiers, getSdkIdentifiers, getAllSdkIdentifiers, _resetSdkRegistry } from './common/sdk-registry.js';
6
- export { getConfig, getConfigSync, preloadConfig, loadConfigToProcessEnv, _resetConfigCache } from './common/config.js';
7
- export { registerRoute, matchRoute, getRegisteredRoutes, clearRouteRegistry, lockRouteRegistry, unlockRouteRegistry, RawRouteErrors, type RawRouteOptions, type HttpMethod, type RegisteredRoute, } from './raw-route.js';
1
+ export { type ConfigValue, config, isConfig, isManagedValue, isSecret, type ManagedValue, type SecretValue, secret, type ValueKind, } from '@aws-blocks/hosting';
2
+ export { type ApiHandler, ApiNamespace, type BlocksContext } from './api.js';
3
+ export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, preloadConfig } from './common/config.js';
4
+ export { type BuildingBlockMeta, Scope, type ScopeOptions, type ScopeParent } from './common/index.js';
5
+ export { _resetSdkRegistry, getAllSdkIdentifiers, getSdkIdentifiers, registerSdkIdentifiers, } from './common/sdk-registry.js';
6
+ export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
7
+ export { ApiError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
8
+ export { clearRouteRegistry, getRegisteredRoutes, type HttpMethod, lockRouteRegistry, matchRoute, RawRouteErrors, type RawRouteOptions, type RegisteredRoute, registerRoute, unlockRouteRegistry, } from './raw-route.js';
8
9
  export { RawRoute } from './raw-route.mock.js';
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACvG,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC9H,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACxH,OAAO,EACL,aAAa,EACb,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,KAAK,WAAW,EAChB,MAAM,EACN,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,MAAM,EACN,KAAK,SAAS,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,aAAa,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxH,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACvG,OAAO,EACN,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,GACtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,UAAU,EACf,iBAAiB,EACjB,UAAU,EACV,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,aAAa,EACb,mBAAmB,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC"}