@aws-blocks/core 0.1.18 → 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.
- package/dist/cdk/__fixtures__/import-meta-handler.d.ts +6 -0
- package/dist/cdk/__fixtures__/import-meta-handler.d.ts.map +1 -0
- package/dist/cdk/__fixtures__/import-meta-handler.js +13 -0
- package/dist/cdk/blocks-backend.d.ts +46 -9
- package/dist/cdk/blocks-backend.d.ts.map +1 -1
- package/dist/cdk/blocks-backend.js +66 -67
- package/dist/cdk/blocks-backend.test.js +114 -56
- package/dist/cdk/blocks-defaults.d.ts +70 -0
- package/dist/cdk/blocks-defaults.d.ts.map +1 -0
- package/dist/cdk/blocks-defaults.js +27 -0
- package/dist/cdk/blocks-defaults.test.d.ts +2 -0
- package/dist/cdk/blocks-defaults.test.d.ts.map +1 -0
- package/dist/cdk/blocks-defaults.test.js +24 -0
- package/dist/cdk/blocks-stack.test.js +74 -31
- package/dist/cdk/bundling.d.ts +42 -0
- package/dist/cdk/bundling.d.ts.map +1 -0
- package/dist/cdk/bundling.js +72 -0
- package/dist/cdk/bundling.test.d.ts +2 -0
- package/dist/cdk/bundling.test.d.ts.map +1 -0
- package/dist/cdk/bundling.test.js +80 -0
- package/dist/cdk/compute/compute.d.ts +33 -0
- package/dist/cdk/compute/compute.d.ts.map +1 -0
- package/dist/cdk/compute/compute.js +28 -0
- package/dist/cdk/compute/default-compute-factory.d.ts +33 -0
- package/dist/cdk/compute/default-compute-factory.d.ts.map +1 -0
- package/dist/cdk/compute/default-compute-factory.js +3 -0
- package/dist/cdk/index.d.ts +92 -12
- package/dist/cdk/index.d.ts.map +1 -1
- package/dist/cdk/index.js +141 -25
- package/dist/cdk/internal.d.ts +25 -0
- package/dist/cdk/internal.d.ts.map +1 -0
- package/dist/cdk/internal.js +27 -0
- package/dist/cdk/mixins.d.ts +18 -4
- package/dist/cdk/mixins.d.ts.map +1 -1
- package/dist/cdk/mixins.js +31 -7
- package/dist/cdk/mixins.test.js +45 -0
- package/dist/common/index.d.ts +9 -0
- package/dist/common/index.d.ts.map +1 -1
- package/dist/hosting-secrets.d.ts +48 -0
- package/dist/hosting-secrets.d.ts.map +1 -0
- package/dist/hosting-secrets.js +57 -0
- package/dist/hosting-secrets.test.d.ts +2 -0
- package/dist/hosting-secrets.test.d.ts.map +1 -0
- package/dist/hosting-secrets.test.js +81 -0
- package/dist/hosting.d.ts +88 -6
- package/dist/hosting.d.ts.map +1 -1
- package/dist/hosting.js +83 -24
- package/dist/hosting.test.js +158 -10
- package/dist/index.cdk.d.ts +13 -11
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +13 -8
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/lambda-handler.test.js +66 -0
- package/dist/pipeline/index.d.ts +24 -2
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/index.js +41 -1
- package/dist/pipeline.test.d.ts +2 -0
- package/dist/pipeline.test.d.ts.map +1 -0
- package/dist/pipeline.test.js +47 -0
- package/dist/rpc.d.ts +21 -0
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +46 -3
- package/dist/rpc.test.js +58 -1
- package/dist/scripts/config.d.ts +15 -0
- package/dist/scripts/config.d.ts.map +1 -0
- package/dist/scripts/config.js +29 -0
- package/dist/scripts/deploy.d.ts.map +1 -1
- package/dist/scripts/deploy.js +4 -0
- package/dist/scripts/dev-server.d.ts +8 -0
- package/dist/scripts/dev-server.d.ts.map +1 -1
- package/dist/scripts/dev-server.js +31 -0
- package/dist/scripts/generate-client.d.ts.map +1 -1
- package/dist/scripts/generate-client.js +9 -4
- package/dist/scripts/generate-client.test.js +19 -4
- package/dist/scripts/index.d.ts +13 -10
- package/dist/scripts/index.d.ts.map +1 -1
- package/dist/scripts/index.js +11 -8
- package/dist/scripts/preflight-credentials.d.ts +30 -0
- package/dist/scripts/preflight-credentials.d.ts.map +1 -0
- package/dist/scripts/preflight-credentials.js +108 -0
- package/dist/scripts/preflight-credentials.test.d.ts +2 -0
- package/dist/scripts/preflight-credentials.test.d.ts.map +1 -0
- package/dist/scripts/preflight-credentials.test.js +77 -0
- package/dist/scripts/sandbox-args.test.d.ts +2 -0
- package/dist/scripts/sandbox-args.test.d.ts.map +1 -0
- package/dist/scripts/sandbox-args.test.js +77 -0
- package/dist/scripts/sandbox.d.ts +52 -0
- package/dist/scripts/sandbox.d.ts.map +1 -1
- package/dist/scripts/sandbox.js +92 -15
- package/dist/scripts/secret.d.ts +15 -0
- package/dist/scripts/secret.d.ts.map +1 -0
- package/dist/scripts/secret.js +29 -0
- package/dist/scripts/secret.test.d.ts +2 -0
- package/dist/scripts/secret.test.d.ts.map +1 -0
- package/dist/scripts/secret.test.js +28 -0
- package/dist/scripts/telemetry.test.js +2 -0
- package/dist/scripts/typegen.d.ts +15 -0
- package/dist/scripts/typegen.d.ts.map +1 -0
- package/dist/scripts/typegen.js +16 -0
- package/dist/secret-naming.d.ts +58 -0
- package/dist/secret-naming.d.ts.map +1 -0
- package/dist/secret-naming.js +67 -0
- package/dist/secret-naming.test.d.ts +2 -0
- package/dist/secret-naming.test.d.ts.map +1 -0
- package/dist/secret-naming.test.js +40 -0
- package/dist/telemetry/environment.d.ts.map +1 -1
- package/dist/telemetry/environment.js +3 -1
- package/dist/telemetry/telemetry.test.js +10 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -1
- package/src/cdk/__fixtures__/import-meta-handler.ts +16 -0
- package/src/cdk/blocks-backend.test.ts +308 -237
- package/src/cdk/blocks-backend.ts +98 -77
- package/src/cdk/blocks-defaults.test.ts +27 -0
- package/src/cdk/blocks-defaults.ts +75 -0
- package/src/cdk/blocks-stack.test.ts +160 -110
- package/src/cdk/bundling.test.ts +90 -0
- package/src/cdk/bundling.ts +76 -0
- package/src/cdk/compute/compute.ts +37 -0
- package/src/cdk/compute/default-compute-factory.ts +37 -0
- package/src/cdk/index.ts +178 -27
- package/src/cdk/internal.ts +30 -0
- package/src/cdk/mixins.test.ts +56 -1
- package/src/cdk/mixins.ts +32 -7
- package/src/common/index.ts +9 -0
- package/src/hosting-secrets.test.ts +106 -0
- package/src/hosting-secrets.ts +106 -0
- package/src/hosting.test.ts +244 -63
- package/src/hosting.ts +180 -68
- package/src/index.cdk.ts +79 -36
- package/src/index.ts +35 -16
- package/src/lambda-handler.test.ts +74 -0
- package/src/pipeline/index.ts +64 -11
- package/src/pipeline.test.ts +55 -0
- package/src/rpc.test.ts +66 -1
- package/src/rpc.ts +60 -3
- package/src/scripts/config.ts +35 -0
- package/src/scripts/deploy.ts +5 -0
- package/src/scripts/dev-server.ts +40 -0
- package/src/scripts/generate-client.test.ts +43 -8
- package/src/scripts/generate-client.ts +70 -65
- package/src/scripts/index.ts +20 -17
- package/src/scripts/preflight-credentials.test.ts +110 -0
- package/src/scripts/preflight-credentials.ts +131 -0
- package/src/scripts/sandbox-args.test.ts +92 -0
- package/src/scripts/sandbox.ts +102 -15
- package/src/scripts/secret.test.ts +35 -0
- package/src/scripts/secret.ts +35 -0
- package/src/scripts/telemetry.test.ts +2 -0
- package/src/scripts/typegen.ts +18 -0
- package/src/secret-naming.test.ts +59 -0
- package/src/secret-naming.ts +99 -0
- package/src/telemetry/environment.ts +3 -1
- package/src/telemetry/telemetry.test.ts +12 -0
- 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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
/**
|
|
203
|
-
|
|
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;
|
|
@@ -399,7 +455,44 @@ export class Hosting extends Construct {
|
|
|
399
455
|
/** SNS topic for hosting CloudWatch alarms (present when `monitoring.enabled` is true). */
|
|
400
456
|
public readonly monitoringTopic?: cdk.aws_sns.ITopic;
|
|
401
457
|
|
|
402
|
-
|
|
458
|
+
/**
|
|
459
|
+
* Async constructor. Required when a `secret()` / `config()` value resolves at
|
|
460
|
+
* **synth time** — i.e. a `domain.domainName` marker — because that value is
|
|
461
|
+
* fetched from its store (Secrets Manager / SSM) during synthesis. Returns a
|
|
462
|
+
* fully-constructed {@link Hosting}.
|
|
463
|
+
*
|
|
464
|
+
* For runtime `environment` markers (read via `getSecret`/`getConfig`) and
|
|
465
|
+
* plain literals, `new Hosting(...)` works directly; `create()` is also safe
|
|
466
|
+
* to use uniformly.
|
|
467
|
+
*
|
|
468
|
+
* @example
|
|
469
|
+
* ```ts
|
|
470
|
+
* await Hosting.create(stack, 'Web', {
|
|
471
|
+
* root: '.', framework: 'nextjs', api: blocksStack,
|
|
472
|
+
* domain: { domainName: config('DOMAIN_PROD') },
|
|
473
|
+
* });
|
|
474
|
+
* ```
|
|
475
|
+
*/
|
|
476
|
+
static async create(scope: Construct, id: string, props: HostingProps): Promise<Hosting> {
|
|
477
|
+
const storeConfig = { secretStore: props.secretStore, configStore: props.configStore };
|
|
478
|
+
const synthMarkers = collectSynthMarkers(props.domain?.domainName);
|
|
479
|
+
const resolved = synthMarkers.length
|
|
480
|
+
? await resolveSecretsAtSynth(synthMarkers, undefined, storeConfig)
|
|
481
|
+
: new Map<string, string>();
|
|
482
|
+
// Fail the deploy early if a declared `environment` secret/config has no value
|
|
483
|
+
// set — existence only, never fetching the value. (Domain markers above already
|
|
484
|
+
// fail this way; this extends it to runtime markers.)
|
|
485
|
+
await assertMarkersExistAtSynth(partitionEnvironment(props.environment).managed, storeConfig);
|
|
486
|
+
return new Hosting(scope, id, props, resolved);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* @param resolvedSecrets - Synth-time-resolved secret values, keyed by secret
|
|
491
|
+
* key. Populated by {@link Hosting.create}; empty for the direct
|
|
492
|
+
* `new Hosting()` path (which then rejects any synth-time secret with a
|
|
493
|
+
* clear "use Hosting.create()" error).
|
|
494
|
+
*/
|
|
495
|
+
constructor(scope: Construct, id: string, props: HostingProps, resolvedSecrets: Map<string, string> = new Map()) {
|
|
403
496
|
super(scope, id);
|
|
404
497
|
|
|
405
498
|
const root = resolve(props.root);
|
|
@@ -415,7 +508,7 @@ export class Hosting extends Construct {
|
|
|
415
508
|
// from breaking frontend builds (e.g., Next.js webpack module resolution).
|
|
416
509
|
const nodeOptions = (process.env.NODE_OPTIONS || '')
|
|
417
510
|
.split(/\s+/)
|
|
418
|
-
.filter(opt => opt !== '--conditions=cdk')
|
|
511
|
+
.filter((opt) => opt !== '--conditions=cdk')
|
|
419
512
|
.join(' ');
|
|
420
513
|
const buildEnv = { ...process.env, NODE_OPTIONS: nodeOptions };
|
|
421
514
|
try {
|
|
@@ -430,7 +523,7 @@ export class Hosting extends Construct {
|
|
|
430
523
|
} catch (error) {
|
|
431
524
|
throw new Error(
|
|
432
525
|
`Frontend build failed: ${props.buildCommand}\n` +
|
|
433
|
-
|
|
526
|
+
`Ensure the build command is correct and all dependencies are installed.`,
|
|
434
527
|
);
|
|
435
528
|
}
|
|
436
529
|
}
|
|
@@ -445,7 +538,7 @@ export class Hosting extends Construct {
|
|
|
445
538
|
if (!existsSync(expectedOutputDir)) {
|
|
446
539
|
throw new Error(
|
|
447
540
|
`Build output directory not found: ${expectedOutputDir}\n` +
|
|
448
|
-
|
|
541
|
+
`Provide a buildCommand or ensure the directory exists before synthesis.`,
|
|
449
542
|
);
|
|
450
543
|
}
|
|
451
544
|
|
|
@@ -494,8 +587,7 @@ export class Hosting extends Construct {
|
|
|
494
587
|
// errorPages will drive the CloudFront configuration correctly,
|
|
495
588
|
// and the HTML files are already in the static assets directory
|
|
496
589
|
// (copied by the adapter).
|
|
497
|
-
const manifestHasErrorPages = manifest.errorPages !== undefined &&
|
|
498
|
-
Object.keys(manifest.errorPages).length > 0;
|
|
590
|
+
const manifestHasErrorPages = manifest.errorPages !== undefined && Object.keys(manifest.errorPages).length > 0;
|
|
499
591
|
const skipPropsErrorPages = !!(props.errorPages && manifestHasErrorPages);
|
|
500
592
|
|
|
501
593
|
// ── 5. Write placeholder config.json into static assets ─────────
|
|
@@ -506,10 +598,7 @@ export class Hosting extends Construct {
|
|
|
506
598
|
const staticDir = manifest.staticAssets.directory;
|
|
507
599
|
const blocksSandboxDir = join(staticDir, '.blocks-sandbox');
|
|
508
600
|
mkdirSync(blocksSandboxDir, { recursive: true });
|
|
509
|
-
writeFileSync(
|
|
510
|
-
join(blocksSandboxDir, 'config.json'),
|
|
511
|
-
JSON.stringify({ _placeholder: true }),
|
|
512
|
-
);
|
|
601
|
+
writeFileSync(join(blocksSandboxDir, 'config.json'), JSON.stringify({ _placeholder: true }));
|
|
513
602
|
|
|
514
603
|
// ── 5a. Mark config.json as a no-cache path ─────────────────────
|
|
515
604
|
// config.json is a fixed-name, mutable runtime-config file: it must
|
|
@@ -532,7 +621,7 @@ export class Hosting extends Construct {
|
|
|
532
621
|
target: 'static',
|
|
533
622
|
};
|
|
534
623
|
// Insert before any catch-all route
|
|
535
|
-
const catchAllIdx = manifest.routes.findIndex((r) => r.pattern === '/*' || r.pattern === '/(.*)')
|
|
624
|
+
const catchAllIdx = manifest.routes.findIndex((r) => r.pattern === '/*' || r.pattern === '/(.*)');
|
|
536
625
|
if (catchAllIdx >= 0) {
|
|
537
626
|
manifest.routes.splice(catchAllIdx, 0, blocksConfigRoute);
|
|
538
627
|
} else {
|
|
@@ -550,28 +639,48 @@ export class Hosting extends Construct {
|
|
|
550
639
|
const normalizedCompute = props.compute
|
|
551
640
|
? {
|
|
552
641
|
...props.compute,
|
|
553
|
-
timeout:
|
|
554
|
-
|
|
555
|
-
|
|
642
|
+
timeout:
|
|
643
|
+
typeof props.compute.timeout === 'number'
|
|
644
|
+
? cdk.Duration.seconds(props.compute.timeout)
|
|
645
|
+
: props.compute.timeout,
|
|
646
|
+
}
|
|
647
|
+
: undefined;
|
|
648
|
+
|
|
649
|
+
// ── 6b. Resolve secret() markers ─────────────────────────────
|
|
650
|
+
// Env values split into plain / runtime-secret / deploy-time buckets;
|
|
651
|
+
// domain names may contain markers resolved at synth time. See
|
|
652
|
+
// ./hosting-secrets.ts for the two-strategy rationale.
|
|
653
|
+
const { plain: plainEnv, managed, byo } = partitionEnvironment(props.environment);
|
|
654
|
+
|
|
655
|
+
// Resolved domain (markers → literals); falls back to the raw config when
|
|
656
|
+
// no domain is set. resolveDomainNames throws a "use Hosting.create()"
|
|
657
|
+
// error if a domain marker reached the sync path unresolved.
|
|
658
|
+
const resolvedDomain = props.domain
|
|
659
|
+
? {
|
|
660
|
+
...props.domain,
|
|
661
|
+
domainName: resolveDomainNames(props.domain.domainName, resolvedSecrets),
|
|
556
662
|
}
|
|
557
663
|
: undefined;
|
|
558
664
|
|
|
559
665
|
const hostingProps: HostingConstructProps = {
|
|
560
666
|
manifest,
|
|
561
667
|
compute: normalizedCompute,
|
|
562
|
-
|
|
668
|
+
// Plain env + deploy-time literals go straight to the L3 (which stays
|
|
669
|
+
// secret-agnostic: it only ever sees plain strings). Runtime secrets are
|
|
670
|
+
// wired separately below so we can attach IAM grants per function.
|
|
671
|
+
environment: Object.keys(plainEnv).length ? plainEnv : undefined,
|
|
672
|
+
domain: resolvedDomain,
|
|
563
673
|
waf: props.waf,
|
|
564
|
-
storage: props.retainOnDelete != null
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
: undefined,
|
|
674
|
+
storage: props.retainOnDelete != null ? { retainOnDelete: props.retainOnDelete } : undefined,
|
|
675
|
+
cdn:
|
|
676
|
+
props.contentSecurityPolicy || props.priceClass || props.geoRestriction || props.quotas
|
|
677
|
+
? {
|
|
678
|
+
contentSecurityPolicy: props.contentSecurityPolicy,
|
|
679
|
+
priceClass: props.priceClass,
|
|
680
|
+
geoRestriction: props.geoRestriction,
|
|
681
|
+
quotas: props.quotas,
|
|
682
|
+
}
|
|
683
|
+
: undefined,
|
|
575
684
|
logging: props.logging,
|
|
576
685
|
buildCache: props.buildCache,
|
|
577
686
|
errorPages: skipPropsErrorPages ? undefined : props.errorPages,
|
|
@@ -591,14 +700,10 @@ export class Hosting extends Construct {
|
|
|
591
700
|
// variables — they surface in computeFunctions as EdgeFunction/IVersion
|
|
592
701
|
// without an `addEnvironment` method. Skip any function that can't take
|
|
593
702
|
// env vars instead of crashing (`fn.addEnvironment is not a function`).
|
|
594
|
-
const canAddEnv = (
|
|
595
|
-
fn: unknown,
|
|
596
|
-
): fn is cdk.aws_lambda.Function =>
|
|
703
|
+
const canAddEnv = (fn: unknown): fn is cdk.aws_lambda.Function =>
|
|
597
704
|
typeof (fn as { addEnvironment?: unknown })?.addEnvironment === 'function';
|
|
598
705
|
|
|
599
|
-
const primaryFunction = [...hosting.computeFunctions.values()].find(
|
|
600
|
-
canAddEnv,
|
|
601
|
-
);
|
|
706
|
+
const primaryFunction = [...hosting.computeFunctions.values()].find(canAddEnv);
|
|
602
707
|
|
|
603
708
|
for (const [, fn] of hosting.computeFunctions) {
|
|
604
709
|
if (!canAddEnv(fn)) continue; // Lambda@Edge: no env var support
|
|
@@ -608,15 +713,25 @@ export class Hosting extends Construct {
|
|
|
608
713
|
if (props.backendConfig) {
|
|
609
714
|
fn.addEnvironment('BLOCKS_CONFIG', JSON.stringify(props.backendConfig));
|
|
610
715
|
}
|
|
716
|
+
// Runtime managed markers (secret|config): inject the store locator
|
|
717
|
+
// (never the value) + grant read/decrypt; read via getSecret()/getConfig().
|
|
718
|
+
// The app's secretStore/configStore (prefix, stage, cacheTtlSeconds) layer
|
|
719
|
+
// over the Blocks defaults.
|
|
720
|
+
const storeConfig = { secretStore: props.secretStore, configStore: props.configStore };
|
|
721
|
+
for (const marker of managed) {
|
|
722
|
+
wireManagedValue(fn, marker, storeConfig);
|
|
723
|
+
}
|
|
724
|
+
// BYO handles (ISecret|IParameter): grant via the handle + inject its locator.
|
|
725
|
+
for (const binding of byo) {
|
|
726
|
+
wireByo(fn, binding, storeConfig);
|
|
727
|
+
}
|
|
611
728
|
}
|
|
612
729
|
|
|
613
730
|
// ── 8. Deploy config.json with resolved CDK tokens ───────────
|
|
614
731
|
const buildId = manifest.buildId;
|
|
615
732
|
if (buildId) {
|
|
616
733
|
const configDeployment = new s3deploy.BucketDeployment(this, 'BlocksConfigDeployment', {
|
|
617
|
-
sources: [
|
|
618
|
-
s3deploy.Source.jsonData('config.json', this.buildConfigJson(props)),
|
|
619
|
-
],
|
|
734
|
+
sources: [s3deploy.Source.jsonData('config.json', this.buildConfigJson(props))],
|
|
620
735
|
destinationBucket: hosting.bucket,
|
|
621
736
|
destinationKeyPrefix: `builds/${buildId}/.blocks-sandbox`,
|
|
622
737
|
prune: false,
|
|
@@ -629,10 +744,7 @@ export class Hosting extends Construct {
|
|
|
629
744
|
// primary guard against staleness is step 5a's no-cache placeholder;
|
|
630
745
|
// this is defense-in-depth so a post-deploy invalidation actually
|
|
631
746
|
// clears any edge entry at its real key.)
|
|
632
|
-
distributionPaths: [
|
|
633
|
-
`/builds/${buildId}/.blocks-sandbox/*`,
|
|
634
|
-
'/.blocks-sandbox/*',
|
|
635
|
-
],
|
|
747
|
+
distributionPaths: [`/builds/${buildId}/.blocks-sandbox/*`, '/.blocks-sandbox/*'],
|
|
636
748
|
cacheControl: [s3deploy.CacheControl.fromString('public, max-age=60, must-revalidate')],
|
|
637
749
|
});
|
|
638
750
|
|
|
@@ -764,8 +876,8 @@ export class Hosting extends Construct {
|
|
|
764
876
|
if (behaviorPattern.endsWith('/*')) {
|
|
765
877
|
console.warn(
|
|
766
878
|
`[Hosting] ⚠️ RawRoute '${route.path}' creates CloudFront behavior '${behaviorPattern}' ` +
|
|
767
|
-
|
|
768
|
-
|
|
879
|
+
`which may shadow SSR/frontend routes under the same prefix. ` +
|
|
880
|
+
`Consider placing this route under ${BLOCKS_RPC_PREFIX}/ to avoid conflicts.`,
|
|
769
881
|
);
|
|
770
882
|
}
|
|
771
883
|
|
package/src/index.cdk.ts
CHANGED
|
@@ -1,46 +1,89 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export {
|
|
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';
|
|
21
|
+
export {
|
|
22
|
+
BlocksBackend,
|
|
23
|
+
type BlocksBackendProps,
|
|
24
|
+
type BlocksDefaults,
|
|
25
|
+
BlocksPresets,
|
|
26
|
+
BlocksStack,
|
|
27
|
+
blocksNodejsBundling,
|
|
28
|
+
type CoreBlocksBackendProps,
|
|
29
|
+
type CoreBlocksStackProps,
|
|
30
|
+
DEFAULT_NODE_RUNTIME,
|
|
31
|
+
finalizeConfigRegistry,
|
|
32
|
+
registerConfig,
|
|
33
|
+
SandboxDisableDeletionProtection,
|
|
34
|
+
Scope,
|
|
35
|
+
SHARED_HANDLER_TIMEOUT_SECONDS,
|
|
36
|
+
synthGuard,
|
|
37
|
+
} from './cdk/index.js';
|
|
38
|
+
export { _resetConfigCache, getConfig, getConfigSync, loadConfigToProcessEnv, preloadConfig } from './common/config.js';
|
|
8
39
|
export { BlocksStackProps } from './common/index.js';
|
|
9
|
-
export {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
40
|
+
export {
|
|
41
|
+
_resetSdkRegistry,
|
|
42
|
+
getAllSdkIdentifiers,
|
|
43
|
+
getSdkIdentifiers,
|
|
44
|
+
registerSdkIdentifiers,
|
|
45
|
+
} from './common/sdk-registry.js';
|
|
46
|
+
export { BLOCKS_AUTH_PREFIX, BLOCKS_RPC_PREFIX } from './constants.js';
|
|
47
|
+
export { ApiError, DEFAULT_API_ERROR_NAME, hasAuthError, isBlocksError } from './errors.js';
|
|
48
|
+
export {
|
|
49
|
+
type BlocksStackApi,
|
|
50
|
+
type ComputeConfig,
|
|
51
|
+
type FrameworkType,
|
|
52
|
+
Hosting,
|
|
53
|
+
type HostingDomainConfig,
|
|
54
|
+
type HostingProps,
|
|
55
|
+
type HostingWafConfig,
|
|
20
56
|
} from './hosting.js';
|
|
57
|
+
export { EventSourceMapping } from './lambda-handler.js';
|
|
58
|
+
export {
|
|
59
|
+
__PIPELINE_STAGE_SCOPE__,
|
|
60
|
+
type BranchConfig,
|
|
61
|
+
DeployStage,
|
|
62
|
+
type DeployStageProps,
|
|
63
|
+
Pipeline,
|
|
64
|
+
type PipelineProps,
|
|
65
|
+
type PipelineSourceConfig,
|
|
66
|
+
type PipelineStageConfig,
|
|
67
|
+
type PipelineSynthConfig,
|
|
68
|
+
} from './pipeline/index.js';
|
|
21
69
|
export {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
70
|
+
type HttpMethod,
|
|
71
|
+
RawRoute,
|
|
72
|
+
RawRouteErrors,
|
|
73
|
+
type RawRouteOptions,
|
|
26
74
|
} from './raw-route.cdk.js';
|
|
27
75
|
export {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
76
|
+
clearRouteRegistry,
|
|
77
|
+
getRegisteredRoutes,
|
|
78
|
+
lockRouteRegistry,
|
|
79
|
+
matchRoute,
|
|
80
|
+
type RegisteredRoute,
|
|
81
|
+
registerRoute,
|
|
82
|
+
unlockRouteRegistry,
|
|
35
83
|
} from './raw-route.js';
|
|
36
84
|
export {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
type PipelineProps,
|
|
43
|
-
type PipelineSourceConfig,
|
|
44
|
-
type PipelineSynthConfig,
|
|
45
|
-
type PipelineStageConfig,
|
|
46
|
-
} from './pipeline/index.js';
|
|
85
|
+
BLOCKS_CONFIG_PARAMETER_PREFIX,
|
|
86
|
+
BLOCKS_SECRET_PARAMETER_PREFIX,
|
|
87
|
+
blocksConfigParameterName,
|
|
88
|
+
blocksSecretParameterName,
|
|
89
|
+
} 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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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, 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';
|
|
@@ -1072,3 +1072,77 @@ describe('createLambdaHandler — sandbox forwarded-host reaches the route', ()
|
|
|
1072
1072
|
assert.strictEqual(capturedHost, 'abc123.execute-api.us-east-1.amazonaws.com');
|
|
1073
1073
|
});
|
|
1074
1074
|
});
|
|
1075
|
+
|
|
1076
|
+
// ── SQS partial batch failure tests ─────────────────────────────────────────
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* These pin the runtime half of the bb-async-job BatchSize fix: with a batch
|
|
1080
|
+
* larger than 1, failing records must be reported individually via
|
|
1081
|
+
* `batchItemFailures` so SQS retries only those messages. If this response
|
|
1082
|
+
* shape regresses, every successful sibling message in a partially-failing
|
|
1083
|
+
* batch would be redelivered (or, worse, the failures silently dropped).
|
|
1084
|
+
*/
|
|
1085
|
+
function makeSqsBatchEvent(count: number) {
|
|
1086
|
+
return {
|
|
1087
|
+
Records: Array.from({ length: count }, (_, i) => ({
|
|
1088
|
+
eventSource: 'aws:sqs',
|
|
1089
|
+
eventSourceARN: 'arn:aws:sqs:us-east-1:123456789012:my-queue',
|
|
1090
|
+
messageId: `msg-${i}`,
|
|
1091
|
+
body: JSON.stringify({ n: i }),
|
|
1092
|
+
})),
|
|
1093
|
+
};
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
describe('handleEventSourceRecords — SQS partial batch responses', () => {
|
|
1097
|
+
it('reports only the failed records of a mixed-success 10-record batch', async () => {
|
|
1098
|
+
const processed: number[] = [];
|
|
1099
|
+
const handlers = new Map<string, (record: any) => Promise<void>>();
|
|
1100
|
+
handlers.set('aws:sqs:my-queue', async (record: any) => {
|
|
1101
|
+
const { n } = JSON.parse(record.body);
|
|
1102
|
+
if (n % 3 === 0) throw new Error(`boom on ${n}`);
|
|
1103
|
+
processed.push(n);
|
|
1104
|
+
});
|
|
1105
|
+
(globalThis as any).__BLOCKS_LAMBDA_EVENT_HANDLERS__ = handlers;
|
|
1106
|
+
|
|
1107
|
+
try {
|
|
1108
|
+
const handler = createLambdaHandler(async () => ({}));
|
|
1109
|
+
const result: any = await handler(makeSqsBatchEvent(10));
|
|
1110
|
+
|
|
1111
|
+
assert.deepStrictEqual(
|
|
1112
|
+
result.batchItemFailures.map((f: any) => f.itemIdentifier).sort(),
|
|
1113
|
+
['msg-0', 'msg-3', 'msg-6', 'msg-9']
|
|
1114
|
+
);
|
|
1115
|
+
assert.deepStrictEqual(processed.sort((a, b) => a - b), [1, 2, 4, 5, 7, 8]);
|
|
1116
|
+
} finally {
|
|
1117
|
+
delete (globalThis as any).__BLOCKS_LAMBDA_EVENT_HANDLERS__;
|
|
1118
|
+
}
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
it('returns no batchItemFailures when every record in the batch succeeds', async () => {
|
|
1122
|
+
const handlers = new Map<string, (record: any) => Promise<void>>();
|
|
1123
|
+
handlers.set('aws:sqs:my-queue', async () => {});
|
|
1124
|
+
(globalThis as any).__BLOCKS_LAMBDA_EVENT_HANDLERS__ = handlers;
|
|
1125
|
+
|
|
1126
|
+
try {
|
|
1127
|
+
const handler = createLambdaHandler(async () => ({}));
|
|
1128
|
+
const result: any = await handler(makeSqsBatchEvent(10));
|
|
1129
|
+
assert.deepStrictEqual(result, {});
|
|
1130
|
+
} finally {
|
|
1131
|
+
delete (globalThis as any).__BLOCKS_LAMBDA_EVENT_HANDLERS__;
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
|
|
1135
|
+
it('reports all records when the whole batch fails (no silent drop)', async () => {
|
|
1136
|
+
const handlers = new Map<string, (record: any) => Promise<void>>();
|
|
1137
|
+
handlers.set('aws:sqs:my-queue', async () => { throw new Error('always'); });
|
|
1138
|
+
(globalThis as any).__BLOCKS_LAMBDA_EVENT_HANDLERS__ = handlers;
|
|
1139
|
+
|
|
1140
|
+
try {
|
|
1141
|
+
const handler = createLambdaHandler(async () => ({}));
|
|
1142
|
+
const result: any = await handler(makeSqsBatchEvent(10));
|
|
1143
|
+
assert.strictEqual(result.batchItemFailures.length, 10);
|
|
1144
|
+
} finally {
|
|
1145
|
+
delete (globalThis as any).__BLOCKS_LAMBDA_EVENT_HANDLERS__;
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
});
|