@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.
- package/README.md +13 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +32 -0
- package/dist/api.test.js +22 -0
- package/dist/bb-utils.d.ts +1 -0
- package/dist/bb-utils.d.ts.map +1 -1
- package/dist/bb-utils.js +3 -0
- package/dist/cdk/apigateway-account.d.ts +33 -0
- package/dist/cdk/apigateway-account.d.ts.map +1 -0
- package/dist/cdk/apigateway-account.js +60 -0
- package/dist/cdk/blocks-backend.d.ts +43 -9
- package/dist/cdk/blocks-backend.d.ts.map +1 -1
- package/dist/cdk/blocks-backend.js +69 -73
- package/dist/cdk/blocks-backend.test.js +78 -69
- package/dist/cdk/blocks-defaults.d.ts +92 -2
- package/dist/cdk/blocks-defaults.d.ts.map +1 -1
- package/dist/cdk/blocks-defaults.js +22 -2
- package/dist/cdk/blocks-defaults.test.js +21 -2
- package/dist/cdk/blocks-stack.test.js +94 -37
- package/dist/cdk/compute/compute-registry.d.ts +19 -0
- package/dist/cdk/compute/compute-registry.d.ts.map +1 -0
- package/dist/cdk/compute/compute-registry.js +38 -0
- package/dist/cdk/compute/compute.d.ts +2 -0
- package/dist/cdk/compute/compute.d.ts.map +1 -1
- package/dist/cdk/compute/compute.js +8 -0
- package/dist/cdk/compute/default-compute-factory.d.ts +34 -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/config-registry.d.ts +34 -4
- package/dist/cdk/config-registry.d.ts.map +1 -1
- package/dist/cdk/config-registry.js +83 -25
- package/dist/cdk/config-registry.test.d.ts +2 -0
- package/dist/cdk/config-registry.test.d.ts.map +1 -0
- package/dist/cdk/config-registry.test.js +115 -0
- package/dist/cdk/index.d.ts +76 -7
- package/dist/cdk/index.d.ts.map +1 -1
- package/dist/cdk/index.js +99 -9
- package/dist/cdk/internal.d.ts +1 -0
- package/dist/cdk/internal.d.ts.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/common/config.d.ts +34 -0
- package/dist/common/config.d.ts.map +1 -1
- package/dist/common/config.js +45 -3
- package/dist/common/config.test.js +19 -0
- package/dist/common/index.d.ts +8 -0
- package/dist/common/index.d.ts.map +1 -1
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -0
- 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 +97 -6
- package/dist/hosting.d.ts.map +1 -1
- package/dist/hosting.js +91 -23
- package/dist/hosting.test.js +182 -10
- package/dist/index.cdk.d.ts +13 -12
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +13 -9
- 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.d.ts +17 -0
- package/dist/lambda-handler.d.ts.map +1 -1
- package/dist/lambda-handler.js +52 -3
- package/dist/lambda-handler.test.js +196 -1
- 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 +34 -0
- package/dist/rpc.test.js +38 -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-empty-buckets.test.d.ts +2 -0
- package/dist/scripts/sandbox-empty-buckets.test.d.ts.map +1 -0
- package/dist/scripts/sandbox-empty-buckets.test.js +171 -0
- package/dist/scripts/sandbox.d.ts +106 -0
- package/dist/scripts/sandbox.d.ts.map +1 -1
- package/dist/scripts/sandbox.js +255 -40
- 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/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/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -1
- package/src/api.test.ts +25 -0
- package/src/api.ts +39 -0
- package/src/bb-utils.ts +3 -0
- package/src/cdk/apigateway-account.ts +66 -0
- package/src/cdk/blocks-backend.test.ts +291 -272
- package/src/cdk/blocks-backend.ts +88 -83
- package/src/cdk/blocks-defaults.test.ts +25 -2
- package/src/cdk/blocks-defaults.ts +94 -2
- package/src/cdk/blocks-stack.test.ts +186 -118
- package/src/cdk/compute/compute-registry.ts +45 -0
- package/src/cdk/compute/compute.ts +10 -0
- package/src/cdk/compute/default-compute-factory.ts +38 -0
- package/src/cdk/config-registry.test.ts +135 -0
- package/src/cdk/config-registry.ts +92 -34
- package/src/cdk/index.ts +129 -10
- package/src/cdk/internal.ts +1 -0
- package/src/client/index.ts +1 -1
- package/src/common/config.test.ts +21 -0
- package/src/common/config.ts +47 -3
- package/src/common/index.ts +8 -0
- package/src/errors.ts +21 -0
- package/src/hosting-secrets.test.ts +106 -0
- package/src/hosting-secrets.ts +106 -0
- package/src/hosting.test.ts +272 -63
- package/src/hosting.ts +200 -68
- package/src/index.cdk.ts +80 -38
- package/src/index.ts +35 -16
- package/src/lambda-handler.test.ts +215 -1
- package/src/lambda-handler.ts +54 -2
- package/src/pipeline/index.ts +64 -11
- package/src/pipeline.test.ts +55 -0
- package/src/rpc.test.ts +44 -1
- package/src/rpc.ts +41 -0
- 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-empty-buckets.test.ts +191 -0
- package/src/scripts/sandbox.ts +287 -39
- package/src/scripts/secret.test.ts +35 -0
- package/src/scripts/secret.ts +35 -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/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`.
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -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 @@
|
|
|
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
|
|
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
|
-
/**
|
|
150
|
-
|
|
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
|
-
|
|
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
|
*
|
package/dist/hosting.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hosting.d.ts","sourceRoot":"","sources":["../src/hosting.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hosting.d.ts","sourceRoot":"","sources":["../src/hosting.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAkB,aAAa,EAAE,gBAAgB,EAAiB,MAAM,gCAAgC,CAAC;AACrH,OAAO,EAAmB,KAAK,kBAAkB,EAAiC,MAAM,8BAA8B,CAAC;AACvH,OAAO,EAIL,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EAC1B,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,GAAG,MAAM,aAAa,CAAC;AAInC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,QAAQ,EAMd,MAAM,sBAAsB,CAAC;AAK9B;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,EAAE,GAAG,CAAC,QAAQ,GAAG,MAAM,CAAC;IAChC,8FAA8F;IAC9F,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;;;;;;;;OAgBG;IACH,iBAAiB,CAAC,EAAE;QAClB,2EAA2E;QAC3E,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC;IACF,uEAAuE;IACvE,YAAY,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC;CAC3C,CAAC;AAEF,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC;AAE3F;;;;;;;GAOG;AACH,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,mBAAmB,EAAE,YAAY,CAAC;IAC5E,8FAA8F;IAC9F,UAAU,EAAE,eAAe,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,mHAAmH;IACnH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAE3B,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;IAE1B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,mEAAmE;IACnE,aAAa,CAAC,EAAE,kBAAkB,CAAC;IAEnC;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,cAAc,CAAC;IAErB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAGxC,uDAAuD;IACvD,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEvC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC;IAEvB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAE/B;;;;OAIG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAE/B,oCAAoC;IACpC,GAAG,CAAC,EAAE,gBAAgB,CAAC;IAEvB,sEAAsE;IACtE,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B,mDAAmD;IACnD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B,wDAAwD;IACxD,UAAU,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC;IAE3C;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE;QACf,IAAI,EAAE,WAAW,GAAG,WAAW,CAAC;QAChC,SAAS,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IAEF;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,MAAM,CAAC,EAAE;QACP,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;;;;;;WAQG;QACH,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IAEF;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,0EAA0E;QAC1E,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC;KAC7B,CAAC;IAEF;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE;QACX,iEAAiE;QACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iEAAiE;QACjE,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAEF;;;OAGG;IACH,OAAO,CAAC,EAAE;QACR,wCAAwC;QACxC,OAAO,EAAE,OAAO,CAAC;QACjB,+CAA+C;QAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IAEF;;;;;;OAMG;IACH,UAAU,CAAC,EAAE;QACX,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,6DAA6D;QAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAEF;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAaD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,qBAAa,OAAQ,SAAQ,SAAS;IACpC,2CAA2C;IAC3C,SAAgB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;IAC1C,mCAAmC;IACnC,SAAgB,YAAY,EAAE,GAAG,CAAC,cAAc,CAAC,YAAY,CAAC;IAC9D,yDAAyD;IACzD,SAAgB,GAAG,EAAE,MAAM,CAAC;IAC5B,gFAAgF;IAChF,SAAgB,WAAW,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC;IACtD,wFAAwF;IACxF,SAAgB,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;IACrD,2FAA2F;IAC3F,SAAgB,eAAe,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;IAErD;;;;;;;;;;;;;;;;;OAiBG;WACU,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAaxF;;;;;OAKG;gBACS,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,eAAe,GAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAa;IA2U/G;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,eAAe;CAwDxB"}
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
? {
|
|
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:
|
|
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
|