@ts-cloud/core 0.5.7 → 0.5.9
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 +60 -29
- package/dist/runtime/adapter.ts +5 -0
- package/dist/runtime-assets/cli-runtime.php +5 -0
- package/dist/types.d.ts +17 -3
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -45597,6 +45597,28 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45597
45597
|
addFunction("CliFunction", functionNames.cli, handlers.cli, "cli", app.cliMemory ?? 1024, app.cliTimeout ?? 900, undefined, app.cliTmpStorage ?? tmpStorage);
|
|
45598
45598
|
if (hasQueue)
|
|
45599
45599
|
addFunction("QueueFunction", functionNames.queue, handlers.queue, "queue", app.queueMemory ?? 1024, app.queueTimeout ?? 120, undefined, app.queueTmpStorage ?? tmpStorage);
|
|
45600
|
+
const pc = (app.provisionedConcurrency ?? 0) > 0 ? app.provisionedConcurrency : 0;
|
|
45601
|
+
const fnLogicalIds = ["HttpFunction", "CliFunction", ...hasQueue ? ["QueueFunction"] : []];
|
|
45602
|
+
if (pc) {
|
|
45603
|
+
for (const L of fnLogicalIds) {
|
|
45604
|
+
resources[`${L}Version`] = {
|
|
45605
|
+
Type: "AWS::Lambda::Version",
|
|
45606
|
+
DeletionPolicy: "Retain",
|
|
45607
|
+
Properties: { FunctionName: Fn2.ref(L) }
|
|
45608
|
+
};
|
|
45609
|
+
resources[`${L}Alias`] = {
|
|
45610
|
+
Type: "AWS::Lambda::Alias",
|
|
45611
|
+
Properties: {
|
|
45612
|
+
FunctionName: Fn2.ref(L),
|
|
45613
|
+
Name: "live",
|
|
45614
|
+
FunctionVersion: Fn2.getAtt(`${L}Version`, "Version"),
|
|
45615
|
+
ProvisionedConcurrencyConfig: { ProvisionedConcurrentExecutions: pc }
|
|
45616
|
+
}
|
|
45617
|
+
};
|
|
45618
|
+
}
|
|
45619
|
+
}
|
|
45620
|
+
const invokeArn = (L) => pc ? Fn2.ref(`${L}Alias`) : Fn2.getAtt(L, "Arn");
|
|
45621
|
+
const invokeName = (L) => pc ? Fn2.ref(`${L}Alias`) : Fn2.ref(L);
|
|
45600
45622
|
resources.HttpApi = {
|
|
45601
45623
|
Type: "AWS::ApiGatewayV2::Api",
|
|
45602
45624
|
Properties: {
|
|
@@ -45609,7 +45631,7 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45609
45631
|
Properties: {
|
|
45610
45632
|
ApiId: Fn2.ref("HttpApi"),
|
|
45611
45633
|
IntegrationType: "AWS_PROXY",
|
|
45612
|
-
IntegrationUri:
|
|
45634
|
+
IntegrationUri: invokeArn("HttpFunction"),
|
|
45613
45635
|
PayloadFormatVersion: "2.0"
|
|
45614
45636
|
}
|
|
45615
45637
|
};
|
|
@@ -45632,7 +45654,7 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45632
45654
|
resources.HttpPermission = {
|
|
45633
45655
|
Type: "AWS::Lambda::Permission",
|
|
45634
45656
|
Properties: {
|
|
45635
|
-
FunctionName:
|
|
45657
|
+
FunctionName: invokeName("HttpFunction"),
|
|
45636
45658
|
Action: "lambda:InvokeFunction",
|
|
45637
45659
|
Principal: "apigateway.amazonaws.com",
|
|
45638
45660
|
SourceArn: Fn2.sub("arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${HttpApi}/*/*")
|
|
@@ -45723,7 +45745,7 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45723
45745
|
Type: "AWS::Lambda::EventSourceMapping",
|
|
45724
45746
|
Properties: {
|
|
45725
45747
|
EventSourceArn: Fn2.getAtt(qId, "Arn"),
|
|
45726
|
-
FunctionName:
|
|
45748
|
+
FunctionName: invokeName("QueueFunction"),
|
|
45727
45749
|
BatchSize: 1,
|
|
45728
45750
|
FunctionResponseTypes: ["ReportBatchItemFailures"],
|
|
45729
45751
|
...concurrency ? { ScalingConfig: { MaximumConcurrency: Math.max(2, concurrency) } } : {}
|
|
@@ -45741,7 +45763,7 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45741
45763
|
State: "ENABLED",
|
|
45742
45764
|
Targets: [{
|
|
45743
45765
|
Id: "cli",
|
|
45744
|
-
Arn:
|
|
45766
|
+
Arn: invokeArn("CliFunction"),
|
|
45745
45767
|
Input: JSON.stringify({ command: "schedule:run" })
|
|
45746
45768
|
}]
|
|
45747
45769
|
}
|
|
@@ -45749,7 +45771,7 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45749
45771
|
resources.SchedulerPermission = {
|
|
45750
45772
|
Type: "AWS::Lambda::Permission",
|
|
45751
45773
|
Properties: {
|
|
45752
|
-
FunctionName:
|
|
45774
|
+
FunctionName: invokeName("CliFunction"),
|
|
45753
45775
|
Action: "lambda:InvokeFunction",
|
|
45754
45776
|
Principal: "events.amazonaws.com",
|
|
45755
45777
|
SourceArn: Fn2.getAtt("SchedulerRule", "Arn")
|
|
@@ -45758,34 +45780,39 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45758
45780
|
}
|
|
45759
45781
|
if (app.warm && app.warm > 0) {
|
|
45760
45782
|
const TARGETS_PER_RULE = 5;
|
|
45761
|
-
const
|
|
45762
|
-
|
|
45763
|
-
for (
|
|
45764
|
-
const
|
|
45765
|
-
|
|
45766
|
-
|
|
45783
|
+
const fnResource = { http: "HttpFunction", queue: "QueueFunction", cli: "CliFunction" };
|
|
45784
|
+
const warmModes = (app.warmFunctions ?? ["http"]).filter((m) => m !== "queue" || hasQueue);
|
|
45785
|
+
for (const mode of warmModes) {
|
|
45786
|
+
const cap = mode.charAt(0).toUpperCase() + mode.slice(1);
|
|
45787
|
+
const ruleCount = Math.ceil(app.warm / TARGETS_PER_RULE);
|
|
45788
|
+
let warmed = 0;
|
|
45789
|
+
for (let r = 0;r < ruleCount; r++) {
|
|
45790
|
+
const targets = Math.min(TARGETS_PER_RULE, app.warm - warmed);
|
|
45791
|
+
resources[`Warmer${cap}Rule${r}`] = {
|
|
45792
|
+
Type: "AWS::Events::Rule",
|
|
45793
|
+
Properties: {
|
|
45794
|
+
Name: `${slug}-${environment}-warmer-${mode}-${r}`,
|
|
45795
|
+
ScheduleExpression: "rate(5 minutes)",
|
|
45796
|
+
State: "ENABLED",
|
|
45797
|
+
Targets: Array.from({ length: targets }, (_, i) => ({
|
|
45798
|
+
Id: `warm-${mode}-${r}-${i}`,
|
|
45799
|
+
Arn: Fn2.getAtt(fnResource[mode], "Arn"),
|
|
45800
|
+
Input: JSON.stringify({ warmer: true })
|
|
45801
|
+
}))
|
|
45802
|
+
}
|
|
45803
|
+
};
|
|
45804
|
+
warmed += targets;
|
|
45805
|
+
}
|
|
45806
|
+
resources[`Warmer${cap}Permission`] = {
|
|
45807
|
+
Type: "AWS::Lambda::Permission",
|
|
45767
45808
|
Properties: {
|
|
45768
|
-
|
|
45769
|
-
|
|
45770
|
-
|
|
45771
|
-
|
|
45772
|
-
Id: `warm-${r}-${i}`,
|
|
45773
|
-
Arn: Fn2.getAtt("HttpFunction", "Arn"),
|
|
45774
|
-
Input: JSON.stringify({ warmer: true })
|
|
45775
|
-
}))
|
|
45809
|
+
FunctionName: Fn2.ref(fnResource[mode]),
|
|
45810
|
+
Action: "lambda:InvokeFunction",
|
|
45811
|
+
Principal: "events.amazonaws.com",
|
|
45812
|
+
SourceArn: Fn2.sub("arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/" + `${slug}-${environment}-warmer-${mode}-*`)
|
|
45776
45813
|
}
|
|
45777
45814
|
};
|
|
45778
|
-
warmed += targets;
|
|
45779
45815
|
}
|
|
45780
|
-
resources.WarmerPermission = {
|
|
45781
|
-
Type: "AWS::Lambda::Permission",
|
|
45782
|
-
Properties: {
|
|
45783
|
-
FunctionName: Fn2.ref("HttpFunction"),
|
|
45784
|
-
Action: "lambda:InvokeFunction",
|
|
45785
|
-
Principal: "events.amazonaws.com",
|
|
45786
|
-
SourceArn: Fn2.sub("arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/" + `${slug}-${environment}-warmer-*`)
|
|
45787
|
-
}
|
|
45788
|
-
};
|
|
45789
45816
|
}
|
|
45790
45817
|
if (cacheEnabled) {
|
|
45791
45818
|
resources.CacheTable = {
|
|
@@ -46407,6 +46434,8 @@ function parseRecordBody(body) {
|
|
|
46407
46434
|
function createQueueHandler(handler8) {
|
|
46408
46435
|
return async (event) => {
|
|
46409
46436
|
const batchItemFailures = [];
|
|
46437
|
+
if (event.warmer)
|
|
46438
|
+
return { batchItemFailures };
|
|
46410
46439
|
if (!handler8)
|
|
46411
46440
|
return { batchItemFailures };
|
|
46412
46441
|
for (const record of event.Records ?? []) {
|
|
@@ -46421,6 +46450,8 @@ function createQueueHandler(handler8) {
|
|
|
46421
46450
|
}
|
|
46422
46451
|
function createCliHandler(handler8) {
|
|
46423
46452
|
return async (event) => {
|
|
46453
|
+
if (event.warmer)
|
|
46454
|
+
return { statusCode: 0, output: "warm" };
|
|
46424
46455
|
if (!handler8)
|
|
46425
46456
|
return { statusCode: 501, output: "No CLI handler configured" };
|
|
46426
46457
|
return handler8(event);
|
package/dist/runtime/adapter.ts
CHANGED
|
@@ -245,6 +245,9 @@ function parseRecordBody(body: string): unknown {
|
|
|
245
245
|
export function createQueueHandler(handler: JobHandler | undefined): LambdaQueueHandler {
|
|
246
246
|
return async (event: SqsEvent): Promise<SqsBatchResponse> => {
|
|
247
247
|
const batchItemFailures: Array<{ itemIdentifier: string }> = []
|
|
248
|
+
// Warmer pings invoke the function directly (no SQS Records) — keep the
|
|
249
|
+
// container warm without treating the ping as a job.
|
|
250
|
+
if ((event as unknown as { warmer?: boolean }).warmer) return { batchItemFailures }
|
|
248
251
|
if (!handler) return { batchItemFailures }
|
|
249
252
|
|
|
250
253
|
for (const record of event.Records ?? []) {
|
|
@@ -268,6 +271,8 @@ export function createQueueHandler(handler: JobHandler | undefined): LambdaQueue
|
|
|
268
271
|
*/
|
|
269
272
|
export function createCliHandler(handler: CommandHandler | undefined): LambdaCliHandler {
|
|
270
273
|
return async (event: CliEvent): Promise<CliResult> => {
|
|
274
|
+
// Warmer pings keep the container warm without running a command.
|
|
275
|
+
if ((event as unknown as { warmer?: boolean }).warmer) return { statusCode: 0, output: 'warm' }
|
|
271
276
|
if (!handler) return { statusCode: 501, output: 'No CLI handler configured' }
|
|
272
277
|
return handler(event)
|
|
273
278
|
}
|
|
@@ -36,6 +36,11 @@ while (true) {
|
|
|
36
36
|
|
|
37
37
|
function dispatch(array $event, string $artisan, string $queueCommand): array
|
|
38
38
|
{
|
|
39
|
+
// Warmer pings keep the container warm without running anything.
|
|
40
|
+
if (($event['warmer'] ?? false) === true) {
|
|
41
|
+
return ['warm' => true];
|
|
42
|
+
}
|
|
43
|
+
|
|
39
44
|
// SQS queue event.
|
|
40
45
|
if (isset($event['Records']) && is_array($event['Records'])) {
|
|
41
46
|
$failures = [];
|
package/dist/types.d.ts
CHANGED
|
@@ -1585,11 +1585,25 @@ export interface ServerlessAppConfig {
|
|
|
1585
1585
|
*/
|
|
1586
1586
|
gatewayVersion?: 1 | 2;
|
|
1587
1587
|
/**
|
|
1588
|
-
*
|
|
1589
|
-
*
|
|
1590
|
-
*
|
|
1588
|
+
* Cheap keep-warm count. Drives a scheduled EventBridge warmer rule that pings
|
|
1589
|
+
* the function(s) every few minutes (the runtime short-circuits warmer pings).
|
|
1590
|
+
* Reduces (does not eliminate) cold starts. For zero cold starts use
|
|
1591
|
+
* {@link provisionedConcurrency}. 0/undefined disables ping-warming.
|
|
1591
1592
|
*/
|
|
1592
1593
|
warm?: number;
|
|
1594
|
+
/**
|
|
1595
|
+
* Which functions the warmer keeps warm. @default ['http']
|
|
1596
|
+
* (queue/cli are async + latency-tolerant, so HTTP-only is the sensible default).
|
|
1597
|
+
*/
|
|
1598
|
+
warmFunctions?: Array<'http' | 'queue' | 'cli'>;
|
|
1599
|
+
/**
|
|
1600
|
+
* Real provisioned concurrency (zero cold starts for N environments). Opts the
|
|
1601
|
+
* app into the alias/version model: each function gets a `live` alias that
|
|
1602
|
+
* traffic routes through, and every deploy publishes a version + flips the
|
|
1603
|
+
* alias. Costs more than `warm` (you pay for the reserved capacity) but
|
|
1604
|
+
* eliminates cold starts. 0/undefined keeps the default $LATEST model.
|
|
1605
|
+
*/
|
|
1606
|
+
provisionedConcurrency?: number;
|
|
1593
1607
|
/** CloudWatch log retention (days) for all function log groups. @default 14 */
|
|
1594
1608
|
logRetention?: number;
|
|
1595
1609
|
/** CLI function memory in MB. @default 1024 */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ts-cloud/core",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.9",
|
|
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.9"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"typescript": "^5.9.3"
|