canopycms-cdk 0.0.68-int.97 → 0.0.68-int.98

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.
@@ -88,10 +88,9 @@ const TRANSFORM_LAMBDA_TIMEOUT = Duration.seconds(30);
88
88
  *
89
89
  * A reservation is a CAP carved from the account's concurrency pool, not
90
90
  * pre-warmed capacity, so it costs nothing when idle (that is
91
- * `provisionedConcurrentExecutions`, which this is not). 10 mirrors the CMS
92
- * Lambda's own reservation: genuine demand is first-render misses only, since
93
- * every already-generated derivative is served by the S3 primary origin without
94
- * invoking this function at all.
91
+ * `provisionedConcurrentExecutions`, which this is not). Genuine demand is
92
+ * first-render misses only, since every already-generated derivative is served
93
+ * by the S3 primary origin without invoking this function at all.
95
94
  */
96
95
  const TRANSFORM_LAMBDA_RESERVED_CONCURRENCY = 10;
97
96
  /**
@@ -21,6 +21,15 @@ import type { CanopyCmsAttachOptions } from './editor-routing.js';
21
21
  * than deploying a configuration that would 504.
22
22
  */
23
23
  export declare const DEFAULT_CMS_LAMBDA_TIMEOUT: Duration;
24
+ /**
25
+ * The cap below which synth warns. With nothing cached, a cold editor load on
26
+ * the example app sends the Lambda about 25 requests, 14 of them chunks
27
+ * requested together, and each invocation holds an execution environment
28
+ * through its cold start.
29
+ */
30
+ export declare const MIN_CMS_RESERVED_CONCURRENCY = 20;
31
+ /** Default `reservedConcurrency`; see that prop for what it caps and costs. */
32
+ export declare const DEFAULT_CMS_RESERVED_CONCURRENCY = 50;
24
33
  export interface CanopyCmsServiceProps {
25
34
  /** Docker image for the CMS Lambda function */
26
35
  cmsDockerImage: lambda.DockerImageCode;
@@ -30,7 +39,24 @@ export interface CanopyCmsServiceProps {
30
39
  memorySize?: number;
31
40
  /** Lambda timeout (default: 60 seconds) */
32
41
  timeout?: Duration;
33
- /** Lambda reserved concurrency cap (default: 10) */
42
+ /**
43
+ * Cap on the CMS Lambda's concurrent invocations (default: 50).
44
+ *
45
+ * Everything the Lambda serves counts against it, including the editor's
46
+ * static chunks (`editorAssetPrefix` on `attachTo`, `/_next/static/*` on
47
+ * `CanopyCmsDistribution`) whenever CloudFront misses them. CloudFront caches
48
+ * per regional edge cache, so after a deploy the first editor load behind
49
+ * each one requests every chunk together, and an invocation over the cap is
50
+ * answered 429: the
51
+ * browser refuses that as a script and the editor fails with
52
+ * `ChunkLoadError`. Synth warns below {@link MIN_CMS_RESERVED_CONCURRENCY}.
53
+ *
54
+ * A reservation costs nothing while idle; it is a ceiling, and a share of
55
+ * the account's concurrency set aside so other functions cannot use it.
56
+ * Lambda keeps 100 of the account's limit unreserved, so this plus every
57
+ * other reservation (AssetSupport's transform Lambda reserves 10) must fit
58
+ * within the account limit minus 100.
59
+ */
34
60
  reservedConcurrency?: number;
35
61
  /**
36
62
  * Lambda architecture (default: `Architecture.ARM_64`, matching the EC2
@@ -1,7 +1,7 @@
1
1
  import * as path from 'node:path';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { Construct } from 'constructs';
4
- import { Duration, RemovalPolicy, Stack, Token, aws_ec2 as ec2, aws_efs as efs, aws_iam as iam, aws_lambda as lambda, aws_autoscaling as autoscaling, aws_s3_assets as s3assets, aws_logs as logs, } from 'aws-cdk-lib';
4
+ import { Annotations, Duration, RemovalPolicy, Stack, Token, aws_ec2 as ec2, aws_efs as efs, aws_iam as iam, aws_lambda as lambda, aws_autoscaling as autoscaling, aws_s3_assets as s3assets, aws_logs as logs, } from 'aws-cdk-lib';
5
5
  import { attachLambdaExecutionPolicies } from './lambda-execution-role.js';
6
6
  import { attachEditorBehaviors } from './editor-routing.js';
7
7
  // This package (`canopycms-cdk`) is `"type": "module"`, so its compiled output
@@ -465,6 +465,15 @@ function assertGitHubAuthProps(props) {
465
465
  * than deploying a configuration that would 504.
466
466
  */
467
467
  export const DEFAULT_CMS_LAMBDA_TIMEOUT = Duration.seconds(60);
468
+ /**
469
+ * The cap below which synth warns. With nothing cached, a cold editor load on
470
+ * the example app sends the Lambda about 25 requests, 14 of them chunks
471
+ * requested together, and each invocation holds an execution environment
472
+ * through its cold start.
473
+ */
474
+ export const MIN_CMS_RESERVED_CONCURRENCY = 20;
475
+ /** Default `reservedConcurrency`; see that prop for what it caps and costs. */
476
+ export const DEFAULT_CMS_RESERVED_CONCURRENCY = 50;
468
477
  /**
469
478
  * Core CDK construct for CanopyCMS deployment.
470
479
  *
@@ -686,6 +695,14 @@ export class CanopyCmsService extends Construct {
686
695
  // Silicon, amd64 on an x86 CI runner) while the function stays x86_64, and
687
696
  // the mismatch only shows at invoke. See `architecture`'s doc comment.
688
697
  const architecture = props.architecture ?? lambda.Architecture.ARM_64;
698
+ const reservedConcurrency = props.reservedConcurrency ?? DEFAULT_CMS_RESERVED_CONCURRENCY;
699
+ if (!Token.isUnresolved(reservedConcurrency) &&
700
+ reservedConcurrency < MIN_CMS_RESERVED_CONCURRENCY) {
701
+ Annotations.of(this).addWarningV2('canopycms:cms-reserved-concurrency-low', `reservedConcurrency is ${reservedConcurrency}, below ${MIN_CMS_RESERVED_CONCURRENCY}. ` +
702
+ `The editor's static chunks are served by this Lambda until CloudFront caches them, ` +
703
+ `and a cold editor load requests them together, so a throttled chunk fails the ` +
704
+ `editor with ChunkLoadError. See the reservedConcurrency prop.`);
705
+ }
689
706
  this.lambdaFunction = new lambda.DockerImageFunction(this, 'CmsFunction', {
690
707
  code: props.cmsDockerImage,
691
708
  // Default (unset) leaves CDK to create the execution role, with its own
@@ -693,7 +710,7 @@ export class CanopyCmsService extends Construct {
693
710
  role: props.lambdaRole,
694
711
  memorySize: props.memorySize ?? 2048,
695
712
  timeout: this.timeout,
696
- reservedConcurrentExecutions: props.reservedConcurrency ?? 10,
713
+ reservedConcurrentExecutions: reservedConcurrency,
697
714
  architecture,
698
715
  vpc: this.vpc,
699
716
  vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
@@ -4522,7 +4522,7 @@ var init_config = __esm({
4522
4522
  title: external_exports.string().optional(),
4523
4523
  subtitle: external_exports.string().optional(),
4524
4524
  theme: external_exports.unknown().optional(),
4525
- previewBase: external_exports.record(external_exports.string()).optional(),
4525
+ previewBase: external_exports.record(external_exports.union([external_exports.string(), external_exports.literal(false)])).optional(),
4526
4526
  previewPrefix: previewPrefixSchema.optional(),
4527
4527
  // UI handler functions (runtime only, don't serialize)
4528
4528
  onAccountClick: external_exports.function().returns(external_exports.void()).optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canopycms-cdk",
3
- "version": "0.0.68-int.97",
3
+ "version": "0.0.68-int.98",
4
4
  "description": "AWS CDK constructs and EC2 worker for CanopyCMS deployment",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -31,7 +31,7 @@
31
31
  "peerDependencies": {
32
32
  "aws-cdk-lib": "^2.260.0",
33
33
  "constructs": "^10.0.0",
34
- "canopycms": "0.0.68-int.97"
34
+ "canopycms": "0.0.68-int.98"
35
35
  },
36
36
  "peerDependenciesMeta": {
37
37
  "aws-cdk-lib": {
@@ -56,8 +56,8 @@
56
56
  "tsx": "^4.19.0",
57
57
  "typescript": "^5.6.3",
58
58
  "vitest": "^4.1.0",
59
- "canopycms": "0.0.68-int.97",
60
- "canopycms-auth-clerk": "0.0.68-int.97"
59
+ "canopycms": "0.0.68-int.98",
60
+ "canopycms-auth-clerk": "0.0.68-int.98"
61
61
  },
62
62
  "scripts": {
63
63
  "build": "rm -rf dist && tsc && node ../../scripts/add-js-extensions.mjs dist",
@@ -12515,7 +12515,7 @@ var init_config2 = __esm({
12515
12515
  title: external_exports.string().optional(),
12516
12516
  subtitle: external_exports.string().optional(),
12517
12517
  theme: external_exports.unknown().optional(),
12518
- previewBase: external_exports.record(external_exports.string()).optional(),
12518
+ previewBase: external_exports.record(external_exports.union([external_exports.string(), external_exports.literal(false)])).optional(),
12519
12519
  previewPrefix: previewPrefixSchema.optional(),
12520
12520
  // UI handler functions (runtime only, don't serialize)
12521
12521
  onAccountClick: external_exports.function().returns(external_exports.void()).optional(),
@@ -48958,7 +48958,7 @@ async function writeWorkerStatus(taskDir, report) {
48958
48958
  }
48959
48959
 
48960
48960
  // ../canopycms/src/version.ts
48961
- var CANOPYCMS_VERSION = "0.0.68-int.97";
48961
+ var CANOPYCMS_VERSION = "0.0.68-int.98";
48962
48962
 
48963
48963
  // ../canopycms/src/worker/log.ts
48964
48964
  init_logger();