@fjall/components-infrastructure 22.0.0 → 24.0.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/lib/patterns/aws/account.d.ts +15 -0
- package/dist/lib/patterns/aws/account.js +18 -3
- package/dist/lib/patterns/aws/cdn.d.ts +2 -4
- package/dist/lib/patterns/aws/cdn.js +15 -2
- package/dist/lib/patterns/aws/interfaces/pattern.d.ts +4 -1
- package/dist/lib/patterns/aws/staticSite.d.ts +1 -1
- package/dist/lib/patterns/aws/staticSite.js +6 -1
- package/dist/lib/resources/aws/cdn/cloudFront.d.ts +22 -7
- package/dist/lib/resources/aws/cdn/cloudFront.js +47 -14
- package/dist/lib/utils/validationLogger.js +9 -11
- package/package.json +3 -3
|
@@ -55,6 +55,21 @@ export interface AccountProps extends StackProps {
|
|
|
55
55
|
*/
|
|
56
56
|
machineTrustMode?: MachineTrustMode;
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* `fjall:description` values for the governance tiers — the same user-facing
|
|
60
|
+
* gold-plating the domain-side constructs carry, so a governance stack's
|
|
61
|
+
* resources explain themselves in the console exactly like a zone or
|
|
62
|
+
* certificate does. Keyed by `organisationType` so each tier self-describes
|
|
63
|
+
* from ONE stamp in this base class.
|
|
64
|
+
*
|
|
65
|
+
* CHARSET CONTRACT: every value must stay inside the AWS tag-value pattern
|
|
66
|
+
* `^[\p{L}\p{Z}\p{N}_.:/=+\-@]*$` — no commas. IAM roles and nested-stack
|
|
67
|
+
* resources (AWS::CloudFormation::Stack) validate tag values against it, so
|
|
68
|
+
* one comma aborts the whole governance deploy at changeset creation ("Tag
|
|
69
|
+
* [fjall:description] contained invalid characters"). Exported so the
|
|
70
|
+
* governanceTagging suite pins the contract for every tier.
|
|
71
|
+
*/
|
|
72
|
+
export declare const GOVERNANCE_TIER_DESCRIPTIONS: Record<OrganisationType, string>;
|
|
58
73
|
export declare class Account extends Stack {
|
|
59
74
|
readonly organisationType: OrganisationType;
|
|
60
75
|
protected readonly resolvedRegion: string;
|
|
@@ -22,9 +22,16 @@ import { InspectorEnablement } from "../../config/aws/inspectorEnablement.js";
|
|
|
22
22
|
* resources explain themselves in the console exactly like a zone or
|
|
23
23
|
* certificate does. Keyed by `organisationType` so each tier self-describes
|
|
24
24
|
* from ONE stamp in this base class.
|
|
25
|
+
*
|
|
26
|
+
* CHARSET CONTRACT: every value must stay inside the AWS tag-value pattern
|
|
27
|
+
* `^[\p{L}\p{Z}\p{N}_.:/=+\-@]*$` — no commas. IAM roles and nested-stack
|
|
28
|
+
* resources (AWS::CloudFormation::Stack) validate tag values against it, so
|
|
29
|
+
* one comma aborts the whole governance deploy at changeset creation ("Tag
|
|
30
|
+
* [fjall:description] contained invalid characters"). Exported so the
|
|
31
|
+
* governanceTagging suite pins the contract for every tier.
|
|
25
32
|
*/
|
|
26
|
-
const GOVERNANCE_TIER_DESCRIPTIONS = {
|
|
27
|
-
organisation: "Fjall-managed organisation governance: org root
|
|
33
|
+
export const GOVERNANCE_TIER_DESCRIPTIONS = {
|
|
34
|
+
organisation: "Fjall-managed organisation governance: org root and member accounts under organisation-wide policy",
|
|
28
35
|
platform: "Fjall-managed platform governance: shared platform services and CI trust",
|
|
29
36
|
account: "Fjall-managed account governance: account security and operations baseline"
|
|
30
37
|
};
|
|
@@ -49,9 +56,17 @@ export class Account extends Stack {
|
|
|
49
56
|
// so `this.organisationType` here would say "account" for every tier (the
|
|
50
57
|
// same trap the connector-mode note below documents). By synth the field
|
|
51
58
|
// is final and the tag renders the true tier.
|
|
59
|
+
//
|
|
60
|
+
// excludeResourceTypes is load-bearing: stack-level tags travel on the
|
|
61
|
+
// CloudFormation API call as literal strings, so a Lazy token there
|
|
62
|
+
// aborts every governance deploy at changeset creation ("Tag
|
|
63
|
+
// [fjall:description] contained invalid characters"). Resources render
|
|
64
|
+
// the resolved value in-template; only the stack's own tag set is
|
|
65
|
+
// skipped — CloudFormation would re-propagate a stack tag to those same
|
|
66
|
+
// resources anyway.
|
|
52
67
|
Tags.of(this).add("fjall:description", Lazy.string({
|
|
53
68
|
produce: () => GOVERNANCE_TIER_DESCRIPTIONS[this.organisationType]
|
|
54
|
-
}));
|
|
69
|
+
}), { excludeResourceTypes: ["aws:cdk:stack"] });
|
|
55
70
|
this.resolvedRegion = region ?? this.region;
|
|
56
71
|
const orgId = this.node.tryGetContext(CDK_CONTEXT_KEYS.ORG_ID);
|
|
57
72
|
if (orgId) {
|
|
@@ -98,7 +98,8 @@ export interface S3CdnProps extends BaseCdnProps {
|
|
|
98
98
|
originAccess?: "oai" | "oac";
|
|
99
99
|
/** Object served for `/` (static sites: "index.html"). */
|
|
100
100
|
defaultRootObject?: string;
|
|
101
|
-
/** "multipage" →
|
|
101
|
+
/** "multipage" (/about → /about.html), "directory" (/about →
|
|
102
|
+
* /about/index.html) or "spa" (403/404 fall back to /index.html). */
|
|
102
103
|
routing?: StaticSiteRouting;
|
|
103
104
|
/** Security-headers policy (built by the caller, e.g. SecurityHeadersPolicy). */
|
|
104
105
|
responseHeadersPolicy?: IResponseHeadersPolicy;
|
|
@@ -193,9 +194,6 @@ export interface SmartCdnBehaviour {
|
|
|
193
194
|
*/
|
|
194
195
|
originRecord?: "alias" | "none";
|
|
195
196
|
}
|
|
196
|
-
/**
|
|
197
|
-
* CDN wrapper class that extends CloudFrontDistribution with Fjall patterns.
|
|
198
|
-
*/
|
|
199
197
|
export declare class Cdn extends CloudFrontDistribution implements ICdn {
|
|
200
198
|
constructor(scope: Construct, id: string, props: ICdnProps);
|
|
201
199
|
private static createEcsOriginResolver;
|
|
@@ -208,6 +208,19 @@ function validateCdnProps(props) {
|
|
|
208
208
|
/**
|
|
209
209
|
* CDN wrapper class that extends CloudFrontDistribution with Fjall patterns.
|
|
210
210
|
*/
|
|
211
|
+
/**
|
|
212
|
+
* What each routing mode asks of the distribution, total over the routing
|
|
213
|
+
* vocabulary — a mode added to `STATIC_SITE_ROUTING_VALUES` fails to compile
|
|
214
|
+
* here until it declares its CDN behaviour, instead of silently synthesising
|
|
215
|
+
* a distribution that ignores it. `multipage` and `directory` rewrite URIs in
|
|
216
|
+
* a viewer-request function; `spa` skips rewriting and relies on 403/404
|
|
217
|
+
* error responses falling back to `/index.html`.
|
|
218
|
+
*/
|
|
219
|
+
const ROUTING_CDN_BEHAVIOUR = {
|
|
220
|
+
multipage: { uriRewrite: "html" },
|
|
221
|
+
directory: { uriRewrite: "directory-index" },
|
|
222
|
+
spa: { spaFallback: true }
|
|
223
|
+
};
|
|
211
224
|
export class Cdn extends CloudFrontDistribution {
|
|
212
225
|
constructor(scope, id, props) {
|
|
213
226
|
validateCdnProps(props);
|
|
@@ -429,8 +442,8 @@ export class Cdn extends CloudFrontDistribution {
|
|
|
429
442
|
const s3Routing = props.originType === "s3"
|
|
430
443
|
? {
|
|
431
444
|
defaultRootObject: props.defaultRootObject,
|
|
432
|
-
|
|
433
|
-
|
|
445
|
+
...(props.routing !== undefined &&
|
|
446
|
+
ROUTING_CDN_BEHAVIOUR[props.routing]),
|
|
434
447
|
responseHeadersPolicy: props.responseHeadersPolicy,
|
|
435
448
|
originRequestPolicy: props.originRequestPolicy
|
|
436
449
|
}
|
|
@@ -452,7 +452,10 @@ export interface IStaticSiteProps {
|
|
|
452
452
|
source: string;
|
|
453
453
|
/** Build command + output directory (single source of truth). */
|
|
454
454
|
build: StaticSiteBuildConfig;
|
|
455
|
-
/** "multipage" (
|
|
455
|
+
/** "multipage" (/about → /about.html), "directory" (/about →
|
|
456
|
+
* /about/index.html — Astro's default layout) or "spa" (unknown paths fall
|
|
457
|
+
* back to /index.html). Match it to the build's emitted layout.
|
|
458
|
+
* Default: "multipage". */
|
|
456
459
|
routing?: StaticSiteRouting;
|
|
457
460
|
/** Security-header configuration. */
|
|
458
461
|
security?: StaticSiteSecurityConfig;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Resources created:
|
|
9
9
|
* - Private S3 bucket + BucketDeployment (asset upload)
|
|
10
|
-
* - CloudFront distribution (OAC,
|
|
10
|
+
* - CloudFront distribution (OAC, routing-mode viewer function, security-headers policy)
|
|
11
11
|
* - ACM certificate + Route53 alias record (when `domain` is set)
|
|
12
12
|
* - Contact-form Lambda + Function URL (when `forms` is set)
|
|
13
13
|
*
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Resources created:
|
|
9
9
|
* - Private S3 bucket + BucketDeployment (asset upload)
|
|
10
|
-
* - CloudFront distribution (OAC,
|
|
10
|
+
* - CloudFront distribution (OAC, routing-mode viewer function, security-headers policy)
|
|
11
11
|
* - ACM certificate + Route53 alias record (when `domain` is set)
|
|
12
12
|
* - Contact-form Lambda + Function URL (when `forms` is set)
|
|
13
13
|
*
|
|
@@ -182,6 +182,11 @@ export class StaticSite extends Construct {
|
|
|
182
182
|
bucket: this._bucket,
|
|
183
183
|
originAccess: "oac",
|
|
184
184
|
defaultRootObject: "index.html",
|
|
185
|
+
// The default VALUE stays "multipage" for configs that omit `routing`,
|
|
186
|
+
// but multipage's own semantics changed with the mode split: the old
|
|
187
|
+
// hybrid fragment served trailing-slash links from `…/index.html`. A
|
|
188
|
+
// deployed directory-layout site that relied on that must now state
|
|
189
|
+
// routing: "directory" — the default cannot protect it.
|
|
185
190
|
routing: this.props.routing ?? "multipage",
|
|
186
191
|
cachePolicy: "CACHING_OPTIMIZED",
|
|
187
192
|
priceClass: "PriceClass_100",
|
|
@@ -38,6 +38,8 @@ export interface AccessGateConfig {
|
|
|
38
38
|
username: string;
|
|
39
39
|
password: string;
|
|
40
40
|
}
|
|
41
|
+
/** See {@link CloudFrontDistributionProps.uriRewrite}. */
|
|
42
|
+
export type UriRewriteMode = "html" | "directory-index";
|
|
41
43
|
export interface CloudFrontDistributionProps {
|
|
42
44
|
/**
|
|
43
45
|
* App this CDN fronts. Keys the cross-stack export a `Domain` resolves via
|
|
@@ -67,9 +69,22 @@ export interface CloudFrontDistributionProps {
|
|
|
67
69
|
accessGate?: false | AccessGateConfig;
|
|
68
70
|
/** Object served for a bare `/` request (static sites: "index.html"). */
|
|
69
71
|
defaultRootObject?: string;
|
|
70
|
-
/**
|
|
71
|
-
*
|
|
72
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Rewrites pretty URLs onto the objects the build actually emitted, via a
|
|
74
|
+
* CloudFront Function. Each mode serves BOTH link shapes (`/about` and
|
|
75
|
+
* `/about/`) from its layout's object:
|
|
76
|
+
*
|
|
77
|
+
* - `"html"` → `/about.html`: builds that emit sibling `.html` files
|
|
78
|
+
* (Astro `build.format: "file"`, Next.js default export).
|
|
79
|
+
* - `"directory-index"` → `/about/index.html`: builds that emit a
|
|
80
|
+
* directory per page (Astro's default `build.format: "directory"`).
|
|
81
|
+
*
|
|
82
|
+
* Dynamic `/api/` paths are carved out so POSTs are not rewritten.
|
|
83
|
+
* Replaces the former `cleanUrls: boolean`, whose single fragment rewrote
|
|
84
|
+
* trailing-slash requests to `…/index.html` and bare requests to `….html`
|
|
85
|
+
* — a hybrid that served neither layout completely.
|
|
86
|
+
*/
|
|
87
|
+
uriRewrite?: UriRewriteMode;
|
|
73
88
|
/** SPA fallback: 403/404 → `/index.html` @200 for client-side routing. */
|
|
74
89
|
spaFallback?: boolean;
|
|
75
90
|
/** Security-headers policy applied to the default + every additional behaviour. */
|
|
@@ -98,14 +113,14 @@ export declare class CloudFrontDistribution extends Construct {
|
|
|
98
113
|
constructor(scope: Construct, id: string, props: CloudFrontDistributionProps);
|
|
99
114
|
/**
|
|
100
115
|
* Build a composable CloudFront Function for VIEWER_REQUEST.
|
|
101
|
-
* Merges accessGate, forwardHostHeader and
|
|
102
|
-
* (CloudFront allows only one function per event type).
|
|
116
|
+
* Merges accessGate, forwardHostHeader and uriRewrite into a single
|
|
117
|
+
* function (CloudFront allows only one function per event type).
|
|
103
118
|
*
|
|
104
119
|
* ORDER IS LOAD-BEARING. The access gate is emitted first because it is the
|
|
105
120
|
* only fragment that can reject a request, and a fragment that returns early
|
|
106
121
|
* ahead of it would silently un-gate whatever paths it short-circuits. The
|
|
107
|
-
*
|
|
108
|
-
*
|
|
122
|
+
* uriRewrite fragments carve `/api/*` out of the rewriting — that carve-out
|
|
123
|
+
* must therefore be a guard around the rewrite, never an early
|
|
109
124
|
* `return request`, or the whole `/api/*` surface leaves the gate behind it.
|
|
110
125
|
*/
|
|
111
126
|
private buildViewerRequestFunction;
|
|
@@ -4,6 +4,43 @@ import { toPascalCase } from "../../../utils/capitaliseString.js";
|
|
|
4
4
|
import { cdnDomainExportName } from "@fjall/util";
|
|
5
5
|
import { Distribution, PriceClass, ViewerProtocolPolicy, CachePolicy, OriginRequestPolicy, AllowedMethods, OriginProtocolPolicy, Function as CloudFrontFunction, FunctionCode, FunctionRuntime, FunctionEventType, OriginAccessIdentity, S3OriginAccessControl } from "aws-cdk-lib/aws-cloudfront";
|
|
6
6
|
import { S3BucketOrigin, LoadBalancerV2Origin, HttpOrigin } from "aws-cdk-lib/aws-cloudfront-origins";
|
|
7
|
+
/**
|
|
8
|
+
* The viewer-request rewrite for each `UriRewriteMode`, keyed on the mode so
|
|
9
|
+
* a new mode fails to compile until it states its rewrite.
|
|
10
|
+
*
|
|
11
|
+
* Both fragments keep the `/api/*` carve-out as a GUARD around the rewrite,
|
|
12
|
+
* never an early `return request` — see the ordering contract on
|
|
13
|
+
* `buildViewerRequestFunction`: a fragment that returns early would let every
|
|
14
|
+
* path it short-circuits leave later fragments behind it.
|
|
15
|
+
*
|
|
16
|
+
* `"html"`: `/about` and `/about/` both → `/about.html`. The trailing slash
|
|
17
|
+
* is stripped before `.html` is appended — the build emitted a FILE, so the
|
|
18
|
+
* old hybrid's `/about/` → `/about/index.html` named an object that does not
|
|
19
|
+
* exist in this layout. The root `/` becomes `/index.html` (both layouts
|
|
20
|
+
* emit one), so neither mode depends on `defaultRootObject` being set.
|
|
21
|
+
*
|
|
22
|
+
* `"directory-index"`: `/about` and `/about/` both → `/about/index.html`,
|
|
23
|
+
* matching the directory-per-page layout (the root `/` likewise becomes
|
|
24
|
+
* `/index.html`).
|
|
25
|
+
*
|
|
26
|
+
* Requests whose last segment carries an extension (`/assets/app.css`) pass
|
|
27
|
+
* through untouched in both modes, and so does any path containing a
|
|
28
|
+
* dot-directory (`/.well-known/apple-app-site-association` must be served
|
|
29
|
+
* byte-for-byte — dot-paths are literal files, never pretty URLs).
|
|
30
|
+
*/
|
|
31
|
+
const URI_REWRITE_FRAGMENTS = {
|
|
32
|
+
html: " var uri = request.uri;" +
|
|
33
|
+
" if (!uri.startsWith('/api/') && uri.indexOf('/.') === -1) {" +
|
|
34
|
+
" if (uri === '/') { request.uri = '/index.html'; }" +
|
|
35
|
+
" else if (uri.endsWith('/')) { request.uri = uri.substring(0, uri.length - 1) + '.html'; }" +
|
|
36
|
+
" else { var seg = uri.substring(uri.lastIndexOf('/') + 1);" +
|
|
37
|
+
" if (seg.indexOf('.') === -1) { request.uri = uri + '.html'; } } }",
|
|
38
|
+
"directory-index": " var uri = request.uri;" +
|
|
39
|
+
" if (!uri.startsWith('/api/') && uri.indexOf('/.') === -1) {" +
|
|
40
|
+
" if (uri.endsWith('/')) { request.uri = uri + 'index.html'; }" +
|
|
41
|
+
" else { var seg = uri.substring(uri.lastIndexOf('/') + 1);" +
|
|
42
|
+
" if (seg.indexOf('.') === -1) { request.uri = uri + '/index.html'; } } }"
|
|
43
|
+
};
|
|
7
44
|
export class CloudFrontDistribution extends Construct {
|
|
8
45
|
id;
|
|
9
46
|
distribution;
|
|
@@ -20,8 +57,8 @@ export class CloudFrontDistribution extends Construct {
|
|
|
20
57
|
const defaultOrigin = this.createOrigin(props.defaultOrigin);
|
|
21
58
|
const defaultCachePolicy = this.resolveCachePolicy(props.defaultCachePolicy);
|
|
22
59
|
// Build composable viewer request function (accessGate + forwardHostHeader
|
|
23
|
-
// +
|
|
24
|
-
const viewerRequestFunction = this.buildViewerRequestFunction(id, props.forwardHostHeader, props.accessGate, props.
|
|
60
|
+
// + uriRewrite)
|
|
61
|
+
const viewerRequestFunction = this.buildViewerRequestFunction(id, props.forwardHostHeader, props.accessGate, props.uriRewrite);
|
|
25
62
|
const functionAssociations = viewerRequestFunction
|
|
26
63
|
? [
|
|
27
64
|
{
|
|
@@ -96,18 +133,18 @@ export class CloudFrontDistribution extends Construct {
|
|
|
96
133
|
}
|
|
97
134
|
/**
|
|
98
135
|
* Build a composable CloudFront Function for VIEWER_REQUEST.
|
|
99
|
-
* Merges accessGate, forwardHostHeader and
|
|
100
|
-
* (CloudFront allows only one function per event type).
|
|
136
|
+
* Merges accessGate, forwardHostHeader and uriRewrite into a single
|
|
137
|
+
* function (CloudFront allows only one function per event type).
|
|
101
138
|
*
|
|
102
139
|
* ORDER IS LOAD-BEARING. The access gate is emitted first because it is the
|
|
103
140
|
* only fragment that can reject a request, and a fragment that returns early
|
|
104
141
|
* ahead of it would silently un-gate whatever paths it short-circuits. The
|
|
105
|
-
*
|
|
106
|
-
*
|
|
142
|
+
* uriRewrite fragments carve `/api/*` out of the rewriting — that carve-out
|
|
143
|
+
* must therefore be a guard around the rewrite, never an early
|
|
107
144
|
* `return request`, or the whole `/api/*` surface leaves the gate behind it.
|
|
108
145
|
*/
|
|
109
|
-
buildViewerRequestFunction(id, forwardHostHeader, accessGate,
|
|
110
|
-
if (!accessGate && !forwardHostHeader &&
|
|
146
|
+
buildViewerRequestFunction(id, forwardHostHeader, accessGate, uriRewrite) {
|
|
147
|
+
if (!accessGate && !forwardHostHeader && uriRewrite === undefined) {
|
|
111
148
|
return undefined;
|
|
112
149
|
}
|
|
113
150
|
const fragments = [];
|
|
@@ -125,12 +162,8 @@ export class CloudFrontDistribution extends Construct {
|
|
|
125
162
|
if (forwardHostHeader) {
|
|
126
163
|
fragments.push(" request.headers['x-forwarded-host'] = { value: request.headers.host.value };");
|
|
127
164
|
}
|
|
128
|
-
if (
|
|
129
|
-
fragments.push(
|
|
130
|
-
" if (!uri.startsWith('/api/')) {" +
|
|
131
|
-
" if (uri.endsWith('/')) { request.uri = uri + 'index.html'; }" +
|
|
132
|
-
" else { var seg = uri.substring(uri.lastIndexOf('/') + 1);" +
|
|
133
|
-
" if (seg.indexOf('.') === -1) { request.uri = uri + '.html'; } } }");
|
|
165
|
+
if (uriRewrite !== undefined) {
|
|
166
|
+
fragments.push(URI_REWRITE_FRAGMENTS[uriRewrite]);
|
|
134
167
|
}
|
|
135
168
|
const functionBody = "function handler(event) { var request = event.request;" +
|
|
136
169
|
fragments.join("") +
|
|
@@ -6,17 +6,8 @@
|
|
|
6
6
|
import { appendFileSync, existsSync, mkdirSync } from "fs";
|
|
7
7
|
import { join } from "path";
|
|
8
8
|
import { fjallLogDir } from "@fjall/util";
|
|
9
|
-
import { FileRotator,
|
|
9
|
+
import { FileRotator, resolveLogRotationBudget } from "@fjall/util/logRotation";
|
|
10
10
|
const LOG_FILENAME = "infrastructure.jsonl";
|
|
11
|
-
/**
|
|
12
|
-
* Shared with the CLI's own logs, so this file cannot outgrow the budget
|
|
13
|
-
* the rest of Fjall's logging keeps to. Without it this log had no bound of
|
|
14
|
-
* any kind: it is appended to on every CDK synth, and one developer machine
|
|
15
|
-
* reached 267MB of it inside a single release cycle.
|
|
16
|
-
*/
|
|
17
|
-
const rotator = new FileRotator({
|
|
18
|
-
maxFiles: LOG_ROTATION_DEFAULTS.MAX_FILES
|
|
19
|
-
});
|
|
20
11
|
/**
|
|
21
12
|
* Write a log entry to the infrastructure JSONL file
|
|
22
13
|
*/
|
|
@@ -36,7 +27,14 @@ function writeToLog(level, message) {
|
|
|
36
27
|
source: "cdk-subprocess"
|
|
37
28
|
});
|
|
38
29
|
const logPath = join(logDir, LOG_FILENAME);
|
|
39
|
-
|
|
30
|
+
// Budget shared with the CLI's own logs — same defaults, same
|
|
31
|
+
// FJALL_LOG_MAX_SIZE / FJALL_LOG_MAX_FILES overrides — so this file
|
|
32
|
+
// cannot outgrow what the rest of Fjall's logging keeps to. It reached
|
|
33
|
+
// 267MB on one developer machine when it had no bound. Resolved per
|
|
34
|
+
// write for the same reason as the log dir above: CDK imports this
|
|
35
|
+
// module long before a command's environment is readable.
|
|
36
|
+
const { maxFileSize, maxFiles } = resolveLogRotationBudget();
|
|
37
|
+
new FileRotator({ maxFiles }).rotateIfNeeded(logPath, maxFileSize);
|
|
40
38
|
appendFileSync(logPath, entry + "\n");
|
|
41
39
|
}
|
|
42
40
|
catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/components-infrastructure",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "24.0.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/fjall-tech/fjall.git",
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
},
|
|
81
81
|
"dependencies": {
|
|
82
82
|
"@aws-sdk/client-organizations": "^3.1098.0",
|
|
83
|
-
"@fjall/generator": "^
|
|
84
|
-
"@fjall/util": "^
|
|
83
|
+
"@fjall/generator": "^24.0.0",
|
|
84
|
+
"@fjall/util": "^24.0.0",
|
|
85
85
|
"constructs": "^10.7.2"
|
|
86
86
|
},
|
|
87
87
|
"overrides": {
|