@ts-cloud/core 0.5.5 → 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 resolveQueueNames(app, slug, env) {
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
- const name = typeof q === "string" ? q : Object.keys(q)[0];
45426
- return `${slug}-${env}-${name}`;
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
- const queueNames = resolveQueueNames(app, slug, environment);
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: 14 }
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
- queueNames.forEach((qName, i) => {
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: qName,
45704
- VisibilityTimeout: Math.max(app.queueTimeout ?? 120, app.queueTimeout ?? 120),
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
- ...app.queueConcurrency ? { ScalingConfig: { MaximumConcurrency: Math.max(2, app.queueConcurrency) } } : {}
45729
+ ...concurrency ? { ScalingConfig: { MaximumConcurrency: Math.max(2, concurrency) } } : {}
45719
45730
  }
45720
45731
  };
45721
- outputs[`QueueUrl${i}`] = { Description: `Queue URL: ${qName}`, Value: Fn2.ref(qId) };
45732
+ outputs[`QueueUrl${i}`] = { Description: `Queue URL: ${q.name}`, Value: Fn2.ref(qId) };
45722
45733
  });
45723
45734
  }
45724
45735
  if (schedulerEnabled) {
@@ -47111,6 +47122,7 @@ export {
47111
47122
  resolveServerlessArtifactBucketName,
47112
47123
  resolveServerlessAppStackName,
47113
47124
  resolveRegion,
47125
+ resolveQueues,
47114
47126
  resolveQueueNames,
47115
47127
  resolveProjectStackName,
47116
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
- /** Resolve the SQS queue names from the manifest. */
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
- /** API Gateway version: 2 = HTTP API (cheaper, default), 1 = REST API. @default 2 */
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. Sets provisioned concurrency on the HTTP alias, or drives a
1586
- * scheduled warmer rule. 0/undefined disables warming.
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 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ts-cloud/core",
3
- "version": "0.5.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.5"
34
+ "@ts-cloud/aws-types": "0.5.6"
35
35
  },
36
36
  "devDependencies": {
37
37
  "typescript": "^5.9.3"