@ts-cloud/core 0.5.4 → 0.5.6
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/index.js
CHANGED
|
@@ -45416,16 +45416,21 @@ function resolveServerlessRuntime(app) {
|
|
|
45416
45416
|
}
|
|
45417
45417
|
|
|
45418
45418
|
// src/serverless/composer.ts
|
|
45419
|
-
function
|
|
45419
|
+
function resolveQueues(app, slug, env) {
|
|
45420
45420
|
if (app.queues === false)
|
|
45421
45421
|
return [];
|
|
45422
45422
|
if (app.queues === undefined || app.queues === true)
|
|
45423
|
-
return [`${slug}-${env}-default`];
|
|
45423
|
+
return [{ name: `${slug}-${env}-default` }];
|
|
45424
45424
|
return app.queues.map((q) => {
|
|
45425
|
-
|
|
45426
|
-
|
|
45425
|
+
if (typeof q === "string")
|
|
45426
|
+
return { name: `${slug}-${env}-${q}` };
|
|
45427
|
+
const [name, concurrency] = Object.entries(q)[0];
|
|
45428
|
+
return { name: `${slug}-${env}-${name}`, concurrency };
|
|
45427
45429
|
});
|
|
45428
45430
|
}
|
|
45431
|
+
function resolveQueueNames(app, slug, env) {
|
|
45432
|
+
return resolveQueues(app, slug, env).map((q) => q.name);
|
|
45433
|
+
}
|
|
45429
45434
|
function composeServerlessAppTemplate(opts) {
|
|
45430
45435
|
const { app, environment, handlers } = opts;
|
|
45431
45436
|
const slug = opts.config.project.slug;
|
|
@@ -45437,8 +45442,12 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45437
45442
|
queue: `${slug}-${environment}-queue`,
|
|
45438
45443
|
cli: `${slug}-${environment}-cli`
|
|
45439
45444
|
};
|
|
45440
|
-
|
|
45445
|
+
if (app.gatewayVersion === 1)
|
|
45446
|
+
throw new Error("serverless app: `gatewayVersion: 1` (REST API) is not supported — ts-cloud uses API Gateway HTTP API (v2). Remove `gatewayVersion` or set it to 2.");
|
|
45447
|
+
const queues = resolveQueues(app, slug, environment);
|
|
45448
|
+
const queueNames = queues.map((q) => q.name);
|
|
45441
45449
|
const hasQueue = queueNames.length > 0;
|
|
45450
|
+
const logRetention = app.logRetention ?? 14;
|
|
45442
45451
|
const imageMode = app.packaging === "image";
|
|
45443
45452
|
const schedulerEnabled = (app.scheduler ?? "on") !== "off";
|
|
45444
45453
|
const cacheEnabled = (app.cache?.driver ?? "dynamodb") === "dynamodb";
|
|
@@ -45554,7 +45563,7 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45554
45563
|
function addFunction(logicalId, name, handler8, mode, memory, timeout, reservedConcurrency, tmp = tmpStorage) {
|
|
45555
45564
|
resources[`${logicalId}LogGroup`] = {
|
|
45556
45565
|
Type: "AWS::Logs::LogGroup",
|
|
45557
|
-
Properties: { LogGroupName: `/aws/lambda/${name}`, RetentionInDays:
|
|
45566
|
+
Properties: { LogGroupName: `/aws/lambda/${name}`, RetentionInDays: logRetention }
|
|
45558
45567
|
};
|
|
45559
45568
|
const codeProps = imageMode ? {
|
|
45560
45569
|
PackageType: "Image",
|
|
@@ -45695,19 +45704,21 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45695
45704
|
MessageRetentionPeriod: 1209600
|
|
45696
45705
|
}
|
|
45697
45706
|
};
|
|
45698
|
-
|
|
45707
|
+
queues.forEach((q, i) => {
|
|
45699
45708
|
const qId = `AppQueue${i}`;
|
|
45709
|
+
const fnTimeout = app.queueTimeout ?? 120;
|
|
45700
45710
|
resources[qId] = {
|
|
45701
45711
|
Type: "AWS::SQS::Queue",
|
|
45702
45712
|
Properties: {
|
|
45703
|
-
QueueName:
|
|
45704
|
-
VisibilityTimeout: Math.
|
|
45713
|
+
QueueName: q.name,
|
|
45714
|
+
VisibilityTimeout: Math.min(43200, fnTimeout * 6),
|
|
45705
45715
|
RedrivePolicy: {
|
|
45706
45716
|
deadLetterTargetArn: Fn2.getAtt("AppQueueDlq", "Arn"),
|
|
45707
45717
|
maxReceiveCount: app.queueTries ?? 3
|
|
45708
45718
|
}
|
|
45709
45719
|
}
|
|
45710
45720
|
};
|
|
45721
|
+
const concurrency = q.concurrency ?? app.queueConcurrency;
|
|
45711
45722
|
resources[`${qId}Mapping`] = {
|
|
45712
45723
|
Type: "AWS::Lambda::EventSourceMapping",
|
|
45713
45724
|
Properties: {
|
|
@@ -45715,10 +45726,10 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45715
45726
|
FunctionName: Fn2.ref("QueueFunction"),
|
|
45716
45727
|
BatchSize: 1,
|
|
45717
45728
|
FunctionResponseTypes: ["ReportBatchItemFailures"],
|
|
45718
|
-
...
|
|
45729
|
+
...concurrency ? { ScalingConfig: { MaximumConcurrency: Math.max(2, concurrency) } } : {}
|
|
45719
45730
|
}
|
|
45720
45731
|
};
|
|
45721
|
-
outputs[`QueueUrl${i}`] = { Description: `Queue URL: ${
|
|
45732
|
+
outputs[`QueueUrl${i}`] = { Description: `Queue URL: ${q.name}`, Value: Fn2.ref(qId) };
|
|
45722
45733
|
});
|
|
45723
45734
|
}
|
|
45724
45735
|
if (schedulerEnabled) {
|
|
@@ -45833,7 +45844,7 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45833
45844
|
...app.assetDomain ? {
|
|
45834
45845
|
Aliases: [app.assetDomain],
|
|
45835
45846
|
ViewerCertificate: {
|
|
45836
|
-
AcmCertificateArn: app.assetCertificateArn,
|
|
45847
|
+
AcmCertificateArn: app.assetCertificateArn ?? Fn2.ref("AssetsCertificate"),
|
|
45837
45848
|
SslSupportMethod: "sni-only",
|
|
45838
45849
|
MinimumProtocolVersion: "TLSv1.2_2021"
|
|
45839
45850
|
}
|
|
@@ -45842,8 +45853,20 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45842
45853
|
}
|
|
45843
45854
|
};
|
|
45844
45855
|
if (app.assetDomain) {
|
|
45845
|
-
if (!app.assetCertificateArn)
|
|
45846
|
-
|
|
45856
|
+
if (!app.assetCertificateArn) {
|
|
45857
|
+
if (!app.hostedZoneId)
|
|
45858
|
+
throw new Error("serverless app: `assetDomain` requires either `assetCertificateArn` (a us-east-1 ACM cert) or `hostedZoneId` (to auto-issue + validate one). CloudFront only accepts certs from us-east-1.");
|
|
45859
|
+
if (region !== "us-east-1")
|
|
45860
|
+
throw new Error(`serverless app: auto-issuing an asset-domain cert needs a us-east-1 app (CloudFront certs must be us-east-1); this app is in ${region}. Supply a pre-issued us-east-1 \`assetCertificateArn\` instead.`);
|
|
45861
|
+
resources.AssetsCertificate = {
|
|
45862
|
+
Type: "AWS::CertificateManager::Certificate",
|
|
45863
|
+
Properties: {
|
|
45864
|
+
DomainName: app.assetDomain,
|
|
45865
|
+
ValidationMethod: "DNS",
|
|
45866
|
+
DomainValidationOptions: [{ DomainName: app.assetDomain, HostedZoneId: app.hostedZoneId }]
|
|
45867
|
+
}
|
|
45868
|
+
};
|
|
45869
|
+
}
|
|
45847
45870
|
if (app.hostedZoneId) {
|
|
45848
45871
|
resources.AssetsDomainRecord = {
|
|
45849
45872
|
Type: "AWS::Route53::RecordSet",
|
|
@@ -47099,6 +47122,7 @@ export {
|
|
|
47099
47122
|
resolveServerlessArtifactBucketName,
|
|
47100
47123
|
resolveServerlessAppStackName,
|
|
47101
47124
|
resolveRegion,
|
|
47125
|
+
resolveQueues,
|
|
47102
47126
|
resolveQueueNames,
|
|
47103
47127
|
resolveProjectStackName,
|
|
47104
47128
|
resolveManagementDashboardSite,
|
|
@@ -17,9 +17,6 @@ require $taskRoot . '/vendor/autoload.php';
|
|
|
17
17
|
$app = require $taskRoot . '/bootstrap/app.php';
|
|
18
18
|
$kernel = $app->make(\Illuminate\Contracts\Http\Kernel::class);
|
|
19
19
|
|
|
20
|
-
$maintenance = getenv('MAINTENANCE_MODE') === '1';
|
|
21
|
-
$bypassSecret = getenv('MAINTENANCE_BYPASS_SECRET') ?: '';
|
|
22
|
-
|
|
23
20
|
while (true) {
|
|
24
21
|
$ctx = nextInvocation($runtimeApi);
|
|
25
22
|
if ($ctx === null) {
|
|
@@ -27,6 +24,11 @@ while (true) {
|
|
|
27
24
|
}
|
|
28
25
|
[$requestId, $event] = $ctx;
|
|
29
26
|
|
|
27
|
+
// Read maintenance state per-invocation so `cloud down`/`up` reaches warm
|
|
28
|
+
// containers (env is flipped via updateFunctionConfiguration).
|
|
29
|
+
$maintenance = getenv('MAINTENANCE_MODE') === '1';
|
|
30
|
+
$bypassSecret = getenv('MAINTENANCE_BYPASS_SECRET') ?: '';
|
|
31
|
+
|
|
30
32
|
try {
|
|
31
33
|
$response = handle($event, $kernel, $maintenance, $bypassSecret);
|
|
32
34
|
postResponse($runtimeApi, $requestId, $response);
|
|
@@ -24,9 +24,6 @@ for ($i = 0; $i < 50 && !file_exists($socketPath); $i++) {
|
|
|
24
24
|
usleep(100000); // 100ms
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
$maintenance = getenv('MAINTENANCE_MODE') === '1';
|
|
28
|
-
$bypassSecret = getenv('MAINTENANCE_BYPASS_SECRET') ?: '';
|
|
29
|
-
|
|
30
27
|
while (true) {
|
|
31
28
|
// 1. Get the next invocation.
|
|
32
29
|
$ctx = nextInvocation($runtimeApi);
|
|
@@ -35,6 +32,12 @@ while (true) {
|
|
|
35
32
|
}
|
|
36
33
|
[$requestId, $event] = $ctx;
|
|
37
34
|
|
|
35
|
+
// Read maintenance state per-invocation: `cloud down`/`up` flips the env via
|
|
36
|
+
// updateFunctionConfiguration, but warm containers would keep a cold-start
|
|
37
|
+
// value if cached outside the loop (the Node adapter reads it per-request too).
|
|
38
|
+
$maintenance = getenv('MAINTENANCE_MODE') === '1';
|
|
39
|
+
$bypassSecret = getenv('MAINTENANCE_BYPASS_SECRET') ?: '';
|
|
40
|
+
|
|
38
41
|
try {
|
|
39
42
|
$response = handle($event, $fpm, $docRoot, $maintenance, $bypassSecret);
|
|
40
43
|
postResponse($runtimeApi, $requestId, $response);
|
|
@@ -38,6 +38,17 @@ export interface ComposedTemplate {
|
|
|
38
38
|
/** Count of resources by CloudFormation type (for deploy summaries). */
|
|
39
39
|
resourceSummary: Record<string, number>;
|
|
40
40
|
}
|
|
41
|
-
/**
|
|
41
|
+
/** A resolved queue: its full name plus an optional per-queue concurrency cap. */
|
|
42
|
+
export interface ResolvedQueue {
|
|
43
|
+
name: string;
|
|
44
|
+
/** Per-queue max concurrency (`queues: [{ emails: 10 }]`), if specified. */
|
|
45
|
+
concurrency?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the SQS queues from the manifest, preserving any per-queue concurrency
|
|
49
|
+
* (`queues: [{ emails: 10 }]` → `{ name: '…-emails', concurrency: 10 }`).
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveQueues(app: ServerlessAppConfig, slug: string, env: EnvironmentType): ResolvedQueue[];
|
|
52
|
+
/** Resolve just the SQS queue names from the manifest. */
|
|
42
53
|
export declare function resolveQueueNames(app: ServerlessAppConfig, slug: string, env: EnvironmentType): string[];
|
|
43
54
|
export declare function composeServerlessAppTemplate(opts: ComposeOptions): ComposedTemplate;
|
package/dist/types.d.ts
CHANGED
|
@@ -1579,13 +1579,19 @@ export interface ServerlessAppConfig {
|
|
|
1579
1579
|
timeout?: number;
|
|
1580
1580
|
/** Reserved concurrency for the HTTP function. */
|
|
1581
1581
|
concurrency?: number;
|
|
1582
|
-
/**
|
|
1582
|
+
/**
|
|
1583
|
+
* API Gateway version. Only `2` (HTTP API — cheaper, faster) is supported;
|
|
1584
|
+
* `1` (REST API) throws at compose time. @default 2
|
|
1585
|
+
*/
|
|
1583
1586
|
gatewayVersion?: 1 | 2;
|
|
1584
1587
|
/**
|
|
1585
|
-
* Keep-warm count.
|
|
1586
|
-
*
|
|
1588
|
+
* Keep-warm count. Drives a scheduled EventBridge warmer rule that pings the
|
|
1589
|
+
* HTTP function every few minutes (the runtime short-circuits warmer pings).
|
|
1590
|
+
* 0/undefined disables warming.
|
|
1587
1591
|
*/
|
|
1588
1592
|
warm?: number;
|
|
1593
|
+
/** CloudWatch log retention (days) for all function log groups. @default 14 */
|
|
1594
|
+
logRetention?: number;
|
|
1589
1595
|
/** CLI function memory in MB. @default 1024 */
|
|
1590
1596
|
cliMemory?: number;
|
|
1591
1597
|
/** CLI command timeout in seconds (allow room for migrations). @default 900 */
|
|
@@ -1692,10 +1698,16 @@ export interface ServerlessAppConfig {
|
|
|
1692
1698
|
assets?: string;
|
|
1693
1699
|
/**
|
|
1694
1700
|
* Serve assets from a custom CDN host instead of the default CloudFront domain
|
|
1695
|
-
* (Vapor `asset-domain`).
|
|
1701
|
+
* (Vapor `asset-domain`). CloudFront needs a us-east-1 ACM cert: supply one via
|
|
1702
|
+
* {@link assetCertificateArn}, or give {@link hostedZoneId} and (for a us-east-1
|
|
1703
|
+
* app) ts-cloud auto-issues + DNS-validates one.
|
|
1696
1704
|
*/
|
|
1697
1705
|
assetDomain?: string;
|
|
1698
|
-
/**
|
|
1706
|
+
/**
|
|
1707
|
+
* us-east-1 ACM certificate ARN for {@link assetDomain} (CloudFront requirement).
|
|
1708
|
+
* Optional when {@link hostedZoneId} is set and the app is in us-east-1 — the
|
|
1709
|
+
* cert is then auto-issued and DNS-validated.
|
|
1710
|
+
*/
|
|
1699
1711
|
assetCertificateArn?: string;
|
|
1700
1712
|
/** Include dotfiles when uploading assets (Vapor `dot-files-as-assets`). @default false */
|
|
1701
1713
|
dotFilesAsAssets?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ts-cloud/core",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Core CloudFormation generation library for ts-cloud",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.com>",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"typecheck": "tsc --noEmit"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ts-cloud/aws-types": "0.5.
|
|
34
|
+
"@ts-cloud/aws-types": "0.5.6"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"typescript": "^5.9.3"
|