@effortless-aws/cli 0.3.0 → 0.4.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.
Files changed (2) hide show
  1. package/dist/cli/index.js +440 -184
  2. package/package.json +2 -2
package/dist/cli/index.js CHANGED
@@ -8,16 +8,16 @@ var __export = (target, all) => {
8
8
  // src/cli/index.ts
9
9
  import { CliConfig, Command as Command7 } from "@effect/cli";
10
10
  import { NodeContext, NodeRuntime } from "@effect/platform-node";
11
- import { Effect as Effect44 } from "effect";
11
+ import { Effect as Effect45 } from "effect";
12
12
  import { createRequire as createRequire2 } from "module";
13
13
 
14
14
  // src/cli/commands/deploy.ts
15
15
  import { Args, Command } from "@effect/cli";
16
- import { Effect as Effect37, Console as Console3, Logger, LogLevel, Option } from "effect";
17
- import * as path9 from "path";
16
+ import { Effect as Effect38, Console as Console3, Logger, LogLevel, Option } from "effect";
17
+ import * as path10 from "path";
18
18
 
19
19
  // src/deploy/deploy.ts
20
- import { Effect as Effect34, Console as Console2 } from "effect";
20
+ import { Effect as Effect35, Console as Console2 } from "effect";
21
21
 
22
22
  // src/cli/colors.ts
23
23
  var wrap = (code) => (s) => `\x1B[${code}m${s}\x1B[0m`;
@@ -2960,6 +2960,7 @@ var syncFiles = (input) => Effect19.gen(function* () {
2960
2960
  walkDir(sourceDir, "");
2961
2961
  let uploaded = 0;
2962
2962
  let unchanged = 0;
2963
+ const uploadedKeys = [];
2963
2964
  for (const [key, filePath] of localFiles) {
2964
2965
  const content = fs2.readFileSync(filePath);
2965
2966
  const md5 = crypto3.createHash("md5").update(content).digest("hex");
@@ -2980,8 +2981,9 @@ var syncFiles = (input) => Effect19.gen(function* () {
2980
2981
  CacheControl: cacheControl
2981
2982
  });
2982
2983
  uploaded++;
2984
+ uploadedKeys.push(key);
2983
2985
  }
2984
- const keysToDelete = [...existingObjects.keys()].filter((k) => !localFiles.has(k));
2986
+ const keysToDelete = [...existingObjects.keys()].filter((k) => !localFiles.has(k) && !k.startsWith("_effortless/"));
2985
2987
  let deleted = 0;
2986
2988
  if (keysToDelete.length > 0) {
2987
2989
  for (let i = 0; i < keysToDelete.length; i += 1e3) {
@@ -2997,7 +2999,7 @@ var syncFiles = (input) => Effect19.gen(function* () {
2997
2999
  }
2998
3000
  }
2999
3001
  yield* Effect19.logDebug(`S3 sync: ${uploaded} uploaded, ${deleted} deleted, ${unchanged} unchanged`);
3000
- return { uploaded, deleted, unchanged };
3002
+ return { uploaded, deleted, unchanged, uploadedKeys };
3001
3003
  });
3002
3004
  var putObject = (input) => s3_exports.make("put_object", {
3003
3005
  Bucket: input.bucketName,
@@ -4823,14 +4825,217 @@ var deployApp = (input) => Effect29.gen(function* () {
4823
4825
  });
4824
4826
 
4825
4827
  // src/deploy/deploy-static-site.ts
4826
- import { Effect as Effect30 } from "effect";
4828
+ import { Effect as Effect31 } from "effect";
4827
4829
  import { Architecture as Architecture3 } from "@aws-sdk/client-lambda";
4828
4830
  import { execSync as execSync2 } from "child_process";
4831
+ import * as fs5 from "fs";
4832
+ import * as path7 from "path";
4833
+
4834
+ // src/deploy/seo.ts
4835
+ import { Effect as Effect30 } from "effect";
4836
+ import * as fs4 from "fs";
4829
4837
  import * as path6 from "path";
4830
- var deployMiddlewareLambda = (input) => Effect30.gen(function* () {
4838
+ import * as crypto4 from "crypto";
4839
+ import * as os from "os";
4840
+ var collectHtmlPaths = (sourceDir) => {
4841
+ const paths = [];
4842
+ const walk = (dir, prefix) => {
4843
+ for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
4844
+ const key = prefix ? `${prefix}/${entry.name}` : entry.name;
4845
+ if (entry.isDirectory()) {
4846
+ walk(path6.join(dir, entry.name), key);
4847
+ } else if (entry.name.endsWith(".html") || entry.name.endsWith(".htm")) {
4848
+ if (entry.name === "404.html" || entry.name === "500.html") continue;
4849
+ let urlPath = "/" + key;
4850
+ if (urlPath.endsWith("/index.html")) {
4851
+ urlPath = urlPath.slice(0, -"index.html".length);
4852
+ } else if (urlPath.endsWith("/index.htm")) {
4853
+ urlPath = urlPath.slice(0, -"index.htm".length);
4854
+ }
4855
+ paths.push(urlPath);
4856
+ }
4857
+ }
4858
+ };
4859
+ walk(sourceDir, "");
4860
+ return paths.sort();
4861
+ };
4862
+ var generateSitemap = (siteUrl, sourceDir) => {
4863
+ const baseUrl = siteUrl.replace(/\/$/, "");
4864
+ const paths = collectHtmlPaths(sourceDir);
4865
+ const urls = paths.map((urlPath) => ` <url>
4866
+ <loc>${baseUrl}${urlPath}</loc>
4867
+ </url>`).join("\n");
4868
+ return `<?xml version="1.0" encoding="UTF-8"?>
4869
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
4870
+ ${urls}
4871
+ </urlset>
4872
+ `;
4873
+ };
4874
+ var generateRobots = (siteUrl, sitemapName = "sitemap.xml") => {
4875
+ const baseUrl = siteUrl.replace(/\/$/, "");
4876
+ return `User-agent: *
4877
+ Allow: /
4878
+
4879
+ Sitemap: ${baseUrl}/${sitemapName}
4880
+ `;
4881
+ };
4882
+ var createJwt = (serviceAccount) => {
4883
+ const now = Math.floor(Date.now() / 1e3);
4884
+ const header = Buffer.from(JSON.stringify({ alg: "RS256", typ: "JWT" })).toString("base64url");
4885
+ const payload = Buffer.from(
4886
+ JSON.stringify({
4887
+ iss: serviceAccount.client_email,
4888
+ scope: "https://www.googleapis.com/auth/indexing",
4889
+ aud: "https://oauth2.googleapis.com/token",
4890
+ iat: now,
4891
+ exp: now + 3600
4892
+ })
4893
+ ).toString("base64url");
4894
+ const signInput = `${header}.${payload}`;
4895
+ const signature = crypto4.createSign("RSA-SHA256").update(signInput).sign(serviceAccount.private_key, "base64url");
4896
+ return `${signInput}.${signature}`;
4897
+ };
4898
+ var getAccessToken = async (serviceAccount) => {
4899
+ const jwt = createJwt(serviceAccount);
4900
+ const response = await fetch("https://oauth2.googleapis.com/token", {
4901
+ method: "POST",
4902
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
4903
+ body: `grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=${jwt}`
4904
+ });
4905
+ if (!response.ok) {
4906
+ const text = await response.text();
4907
+ throw new Error(`Failed to get Google access token: ${response.status} ${text}`);
4908
+ }
4909
+ const data = await response.json();
4910
+ return data.access_token;
4911
+ };
4912
+ var publishUrl = async (accessToken, url) => {
4913
+ const response = await fetch("https://indexing.googleapis.com/v3/urlNotifications:publish", {
4914
+ method: "POST",
4915
+ headers: {
4916
+ "Content-Type": "application/json",
4917
+ Authorization: `Bearer ${accessToken}`
4918
+ },
4919
+ body: JSON.stringify({ url, type: "URL_UPDATED" })
4920
+ });
4921
+ if (!response.ok) {
4922
+ const text = await response.text();
4923
+ return { url, ok: false, error: `${response.status} ${text}` };
4924
+ }
4925
+ return { url, ok: true };
4926
+ };
4927
+ var collectHtmlKeys = (sourceDir) => {
4928
+ const keys = [];
4929
+ const walk = (dir, prefix) => {
4930
+ for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
4931
+ const fullPath = path6.join(dir, entry.name);
4932
+ const key = prefix ? `${prefix}/${entry.name}` : entry.name;
4933
+ if (entry.isDirectory()) {
4934
+ walk(fullPath, key);
4935
+ } else if (entry.name.endsWith(".html") || entry.name.endsWith(".htm")) {
4936
+ if (entry.name === "404.html" || entry.name === "500.html") continue;
4937
+ keys.push(key);
4938
+ }
4939
+ }
4940
+ };
4941
+ walk(sourceDir, "");
4942
+ return keys;
4943
+ };
4944
+ var keysToUrls = (siteUrl, keys) => {
4945
+ const baseUrl = siteUrl.replace(/\/$/, "");
4946
+ return keys.map((key) => {
4947
+ let urlPath = "/" + key;
4948
+ if (urlPath.endsWith("/index.html")) {
4949
+ urlPath = urlPath.slice(0, -"index.html".length);
4950
+ } else if (urlPath.endsWith("/index.htm")) {
4951
+ urlPath = urlPath.slice(0, -"index.htm".length);
4952
+ }
4953
+ return `${baseUrl}${urlPath}`;
4954
+ });
4955
+ };
4956
+ var INDEXED_URLS_KEY = "_effortless/indexed-urls.json";
4957
+ var loadIndexedUrls = (bucketName) => Effect30.gen(function* () {
4958
+ const result = yield* s3_exports.make("get_object", {
4959
+ Bucket: bucketName,
4960
+ Key: INDEXED_URLS_KEY
4961
+ }).pipe(Effect30.option);
4962
+ if (result._tag === "None") return /* @__PURE__ */ new Set();
4963
+ const body = yield* Effect30.tryPromise({
4964
+ try: () => result.value.Body?.transformToString("utf-8") ?? Promise.resolve("[]"),
4965
+ catch: () => new Error("Failed to read indexed URLs from S3")
4966
+ });
4967
+ const urls = JSON.parse(body);
4968
+ return new Set(urls);
4969
+ });
4970
+ var saveIndexedUrls = (bucketName, urls) => s3_exports.make("put_object", {
4971
+ Bucket: bucketName,
4972
+ Key: INDEXED_URLS_KEY,
4973
+ Body: JSON.stringify([...urls].sort(), null, 2),
4974
+ ContentType: "application/json; charset=utf-8"
4975
+ });
4976
+ var submitToGoogleIndexing = (input) => Effect30.gen(function* () {
4977
+ const { serviceAccountPath, projectDir, bucketName, allPageUrls } = input;
4978
+ const indexedUrls = yield* loadIndexedUrls(bucketName);
4979
+ const urlsToSubmit = allPageUrls.filter((url) => !indexedUrls.has(url));
4980
+ const currentUrlSet = new Set(allPageUrls);
4981
+ for (const url of indexedUrls) {
4982
+ if (!currentUrlSet.has(url)) {
4983
+ indexedUrls.delete(url);
4984
+ }
4985
+ }
4986
+ if (urlsToSubmit.length === 0) {
4987
+ yield* Effect30.logDebug("All pages already indexed, skipping Google Indexing API");
4988
+ return { submitted: 0, failed: 0, skipped: allPageUrls.length };
4989
+ }
4990
+ const expanded = serviceAccountPath.startsWith("~/") ? path6.join(os.homedir(), serviceAccountPath.slice(2)) : serviceAccountPath;
4991
+ const keyPath = path6.resolve(projectDir, expanded);
4992
+ if (!fs4.existsSync(keyPath)) {
4993
+ return yield* Effect30.fail(
4994
+ new Error(`Google service account key not found: ${keyPath}`)
4995
+ );
4996
+ }
4997
+ const serviceAccount = JSON.parse(fs4.readFileSync(keyPath, "utf-8"));
4998
+ if (!serviceAccount.client_email || !serviceAccount.private_key) {
4999
+ return yield* Effect30.fail(
5000
+ new Error(`Invalid service account key: missing client_email or private_key`)
5001
+ );
5002
+ }
5003
+ yield* Effect30.logDebug(`Authenticating with Google as ${serviceAccount.client_email}`);
5004
+ const accessToken = yield* Effect30.tryPromise({
5005
+ try: () => getAccessToken(serviceAccount),
5006
+ catch: (error) => new Error(`Google auth failed: ${error}`)
5007
+ });
5008
+ const maxUrls = Math.min(urlsToSubmit.length, 200);
5009
+ if (urlsToSubmit.length > 200) {
5010
+ yield* Effect30.logDebug(
5011
+ `Google Indexing API daily quota is 200. Submitting first 200 of ${urlsToSubmit.length} URLs.`
5012
+ );
5013
+ }
5014
+ let submitted = 0;
5015
+ let failed = 0;
5016
+ for (const url of urlsToSubmit.slice(0, maxUrls)) {
5017
+ const result = yield* Effect30.tryPromise({
5018
+ try: () => publishUrl(accessToken, url),
5019
+ catch: (error) => new Error(`Failed to submit ${url}: ${error}`)
5020
+ });
5021
+ if (result.ok) {
5022
+ submitted++;
5023
+ indexedUrls.add(url);
5024
+ } else {
5025
+ failed++;
5026
+ yield* Effect30.logDebug(`Failed to index ${result.url}: ${result.error}`);
5027
+ }
5028
+ }
5029
+ yield* saveIndexedUrls(bucketName, indexedUrls);
5030
+ yield* Effect30.logDebug(`Google Indexing: ${submitted} submitted, ${failed} failed, ${allPageUrls.length - urlsToSubmit.length} already indexed`);
5031
+ return { submitted, failed, skipped: allPageUrls.length - urlsToSubmit.length };
5032
+ });
5033
+
5034
+ // src/deploy/deploy-static-site.ts
5035
+ var deployMiddlewareLambda = (input) => Effect31.gen(function* () {
4831
5036
  const { projectDir, project, stage, handlerName, file, exportName, tagCtx } = input;
4832
5037
  const middlewareName = `${handlerName}-middleware`;
4833
- yield* Effect30.logDebug(`Deploying middleware Lambda@Edge: ${middlewareName}`);
5038
+ yield* Effect31.logDebug(`Deploying middleware Lambda@Edge: ${middlewareName}`);
4834
5039
  const roleArn = yield* ensureEdgeRole(
4835
5040
  project,
4836
5041
  stage,
@@ -4856,14 +5061,14 @@ var deployMiddlewareLambda = (input) => Effect30.gen(function* () {
4856
5061
  architecture: Architecture3.x86_64,
4857
5062
  tags: makeTags(tagCtx, "lambda")
4858
5063
  }).pipe(
4859
- Effect30.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5064
+ Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
4860
5065
  );
4861
5066
  const { versionArn } = yield* publishVersion(
4862
5067
  `${project}-${stage}-${middlewareName}`
4863
5068
  ).pipe(
4864
- Effect30.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5069
+ Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
4865
5070
  );
4866
- yield* Effect30.logDebug(`Middleware deployed: ${versionArn}`);
5071
+ yield* Effect31.logDebug(`Middleware deployed: ${versionArn}`);
4867
5072
  return { versionArn };
4868
5073
  });
4869
5074
  var ERROR_PAGE_KEY = "_effortless/404.html";
@@ -4901,7 +5106,7 @@ var generateErrorPageHtml = () => `<!DOCTYPE html>
4901
5106
  </div>
4902
5107
  </body>
4903
5108
  </html>`;
4904
- var deployStaticSite = (input) => Effect30.gen(function* () {
5109
+ var deployStaticSite = (input) => Effect31.gen(function* () {
4905
5110
  const { projectDir, project, region, fn: fn13 } = input;
4906
5111
  const { exportName, config } = fn13;
4907
5112
  const stage = resolveStage(input.stage);
@@ -4910,16 +5115,16 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
4910
5115
  const tagCtx = { project, stage, handler: handlerName };
4911
5116
  const routePatterns = fn13.routePatterns;
4912
5117
  if (routePatterns.length > 0 && !input.apiOriginDomain) {
4913
- return yield* Effect30.fail(
5118
+ return yield* Effect31.fail(
4914
5119
  new Error(
4915
5120
  `Static site "${exportName}" has routes but no API handler was deployed. Ensure defineApi() handlers are included in the discovery patterns.`
4916
5121
  )
4917
5122
  );
4918
5123
  }
4919
5124
  if (config.build) {
4920
- yield* Effect30.logDebug(`Building site: ${config.build}`);
5125
+ yield* Effect31.logDebug(`Building site: ${config.build}`);
4921
5126
  const buildStart = Date.now();
4922
- yield* Effect30.try({
5127
+ yield* Effect31.try({
4923
5128
  try: () => execSync2(config.build, {
4924
5129
  cwd: projectDir,
4925
5130
  stdio: input.verbose ? "inherit" : "pipe"
@@ -4932,7 +5137,7 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
4932
5137
  return new Error(`Site build failed: ${config.build}`);
4933
5138
  }
4934
5139
  });
4935
- yield* Effect30.logDebug(`Site built in ${((Date.now() - buildStart) / 1e3).toFixed(1)}s`);
5140
+ yield* Effect31.logDebug(`Site built in ${((Date.now() - buildStart) / 1e3).toFixed(1)}s`);
4936
5141
  }
4937
5142
  const bucketName = `${project}-${stage}-${handlerName}-site`.toLowerCase();
4938
5143
  yield* ensureBucket({
@@ -4956,10 +5161,10 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
4956
5161
  if (certCoversWww) {
4957
5162
  aliases = [domain, wwwCandidate];
4958
5163
  wwwDomain = wwwCandidate;
4959
- yield* Effect30.logDebug(`ACM certificate covers ${wwwCandidate}, enabling www \u2192 non-www redirect`);
5164
+ yield* Effect31.logDebug(`ACM certificate covers ${wwwCandidate}, enabling www \u2192 non-www redirect`);
4960
5165
  } else {
4961
5166
  aliases = [domain];
4962
- yield* Effect30.logWarning(
5167
+ yield* Effect31.logWarning(
4963
5168
  `ACM certificate does not cover ${wwwCandidate}. For SEO, add ${wwwCandidate} to your ACM certificate in us-east-1 to enable www \u2192 non-www redirect.`
4964
5169
  );
4965
5170
  }
@@ -4980,7 +5185,7 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
4980
5185
  exportName,
4981
5186
  tagCtx
4982
5187
  }).pipe(
4983
- Effect30.provide(clients_exports.makeClients({ iam: { region: "us-east-1" } }))
5188
+ Effect31.provide(clients_exports.makeClients({ iam: { region: "us-east-1" } }))
4984
5189
  );
4985
5190
  lambdaEdgeArn = result.versionArn;
4986
5191
  } else {
@@ -5015,8 +5220,32 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
5015
5220
  ...input.apiOriginDomain && routePatterns.length > 0 ? { apiOriginDomain: input.apiOriginDomain, routePatterns } : {}
5016
5221
  });
5017
5222
  yield* putBucketPolicyForOAC(bucketName, distributionArn);
5018
- const sourceDir = path6.resolve(projectDir, config.dir);
5223
+ const sourceDir = path7.resolve(projectDir, config.dir);
5019
5224
  yield* syncFiles({ bucketName, sourceDir });
5225
+ const seo = config.seo;
5226
+ const siteUrl = domain ? `https://${domain}` : `https://${domainName}`;
5227
+ const seoGenerated = [];
5228
+ if (seo) {
5229
+ const sitemapName = seo.sitemap;
5230
+ if (!fs5.existsSync(path7.join(sourceDir, sitemapName))) {
5231
+ const sitemap = generateSitemap(siteUrl, sourceDir);
5232
+ yield* putObject({
5233
+ bucketName,
5234
+ key: sitemapName,
5235
+ body: sitemap,
5236
+ contentType: "application/xml; charset=utf-8"
5237
+ });
5238
+ seoGenerated.push(sitemapName);
5239
+ }
5240
+ const robots = generateRobots(siteUrl, sitemapName);
5241
+ yield* putObject({
5242
+ bucketName,
5243
+ key: "robots.txt",
5244
+ body: robots,
5245
+ contentType: "text/plain; charset=utf-8"
5246
+ });
5247
+ seoGenerated.push("robots.txt");
5248
+ }
5020
5249
  if (!isSpa && !config.errorPage) {
5021
5250
  yield* putObject({
5022
5251
  bucketName,
@@ -5026,21 +5255,33 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
5026
5255
  });
5027
5256
  }
5028
5257
  yield* invalidateDistribution(distributionId);
5029
- const url = domain ? `https://${domain}` : `https://${domainName}`;
5030
- yield* Effect30.logDebug(`Static site deployed: ${url}`);
5258
+ let indexingResult;
5259
+ if (seo?.googleIndexing) {
5260
+ const allHtmlKeys = collectHtmlKeys(sourceDir);
5261
+ const allPageUrls = keysToUrls(siteUrl, allHtmlKeys);
5262
+ indexingResult = yield* submitToGoogleIndexing({
5263
+ serviceAccountPath: seo.googleIndexing,
5264
+ projectDir,
5265
+ bucketName,
5266
+ allPageUrls
5267
+ });
5268
+ }
5269
+ yield* Effect31.logDebug(`Static site deployed: ${siteUrl}`);
5031
5270
  return {
5032
5271
  exportName,
5033
5272
  handlerName,
5034
- url,
5273
+ url: siteUrl,
5035
5274
  distributionId,
5036
- bucketName
5275
+ bucketName,
5276
+ seoGenerated: seoGenerated.length > 0 ? seoGenerated : void 0,
5277
+ indexingResult
5037
5278
  };
5038
5279
  });
5039
5280
 
5040
5281
  // src/deploy/deploy-fifo-queue.ts
5041
- import { Effect as Effect31 } from "effect";
5282
+ import { Effect as Effect32 } from "effect";
5042
5283
  var FIFO_QUEUE_DEFAULT_PERMISSIONS = ["sqs:*", "logs:*"];
5043
- var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect31.gen(function* () {
5284
+ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect32.gen(function* () {
5044
5285
  const { exportName, config } = fn13;
5045
5286
  const handlerName = exportName;
5046
5287
  const tagCtx = {
@@ -5048,7 +5289,7 @@ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, d
5048
5289
  stage: resolveStage(input.stage),
5049
5290
  handler: handlerName
5050
5291
  };
5051
- yield* Effect31.logDebug("Creating SQS FIFO queue...");
5292
+ yield* Effect32.logDebug("Creating SQS FIFO queue...");
5052
5293
  const queueName = `${input.project}-${tagCtx.stage}-${handlerName}`;
5053
5294
  const timeout = config.timeout ?? 30;
5054
5295
  const { queueUrl, queueArn } = yield* ensureFifoQueue({
@@ -5078,14 +5319,14 @@ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, d
5078
5319
  ...depsPermissions ? { depsPermissions } : {},
5079
5320
  ...staticGlobs && staticGlobs.length > 0 ? { staticGlobs } : {}
5080
5321
  });
5081
- yield* Effect31.logDebug("Setting up SQS event source mapping...");
5322
+ yield* Effect32.logDebug("Setting up SQS event source mapping...");
5082
5323
  yield* ensureSqsEventSourceMapping({
5083
5324
  functionArn,
5084
5325
  queueArn,
5085
5326
  batchSize: config.batchSize ?? 10,
5086
5327
  batchWindow: config.batchWindow
5087
5328
  });
5088
- yield* Effect31.logDebug(`FIFO queue deployment complete! Queue: ${queueUrl}`);
5329
+ yield* Effect32.logDebug(`FIFO queue deployment complete! Queue: ${queueUrl}`);
5089
5330
  return {
5090
5331
  exportName,
5091
5332
  functionArn,
@@ -5096,9 +5337,9 @@ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, d
5096
5337
  });
5097
5338
 
5098
5339
  // src/deploy/deploy-bucket.ts
5099
- import { Effect as Effect32 } from "effect";
5340
+ import { Effect as Effect33 } from "effect";
5100
5341
  var BUCKET_DEFAULT_PERMISSIONS = ["s3:*", "logs:*"];
5101
- var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect32.gen(function* () {
5342
+ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect33.gen(function* () {
5102
5343
  const { exportName, config, hasHandler } = fn13;
5103
5344
  const handlerName = exportName;
5104
5345
  const tagCtx = {
@@ -5106,7 +5347,7 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
5106
5347
  stage: resolveStage(input.stage),
5107
5348
  handler: handlerName
5108
5349
  };
5109
- yield* Effect32.logDebug("Creating S3 bucket...");
5350
+ yield* Effect33.logDebug("Creating S3 bucket...");
5110
5351
  const bucketName = `${input.project}-${tagCtx.stage}-${handlerName}`;
5111
5352
  const { bucketArn } = yield* ensureBucket({
5112
5353
  name: bucketName,
@@ -5114,7 +5355,7 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
5114
5355
  tags: makeTags(tagCtx, "s3-bucket")
5115
5356
  });
5116
5357
  if (!hasHandler) {
5117
- yield* Effect32.logDebug(`Bucket deployment complete (resource-only)! Bucket: ${bucketName}`);
5358
+ yield* Effect33.logDebug(`Bucket deployment complete (resource-only)! Bucket: ${bucketName}`);
5118
5359
  return {
5119
5360
  exportName,
5120
5361
  status: "resource-only",
@@ -5146,7 +5387,7 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
5146
5387
  prefix: config.prefix,
5147
5388
  suffix: config.suffix
5148
5389
  });
5149
- yield* Effect32.logDebug(`Bucket deployment complete! Bucket: ${bucketName}`);
5390
+ yield* Effect33.logDebug(`Bucket deployment complete! Bucket: ${bucketName}`);
5150
5391
  return {
5151
5392
  exportName,
5152
5393
  functionArn,
@@ -5157,8 +5398,8 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
5157
5398
  });
5158
5399
 
5159
5400
  // src/deploy/deploy-mailer.ts
5160
- import { Effect as Effect33, Console } from "effect";
5161
- var deployMailer = ({ project, stage, region, fn: fn13 }) => Effect33.gen(function* () {
5401
+ import { Effect as Effect34, Console } from "effect";
5402
+ var deployMailer = ({ project, stage, region, fn: fn13 }) => Effect34.gen(function* () {
5162
5403
  const { exportName, config } = fn13;
5163
5404
  const handlerName = exportName;
5164
5405
  const resolvedStage = resolveStage(stage);
@@ -5167,7 +5408,7 @@ var deployMailer = ({ project, stage, region, fn: fn13 }) => Effect33.gen(functi
5167
5408
  stage: resolvedStage,
5168
5409
  handler: handlerName
5169
5410
  };
5170
- yield* Effect33.logDebug(`Ensuring SES identity for ${config.domain}...`);
5411
+ yield* Effect34.logDebug(`Ensuring SES identity for ${config.domain}...`);
5171
5412
  const { domain, verified, dkimRecords } = yield* ensureSesIdentity({
5172
5413
  domain: config.domain,
5173
5414
  tags: makeTags(tagCtx, "ses")
@@ -5231,7 +5472,7 @@ var createLiveProgress = (manifest) => {
5231
5472
  const sec = ((Date.now() - startTime) / 1e3).toFixed(1);
5232
5473
  return c.dim(`${sec}s`);
5233
5474
  };
5234
- return (name, type, status) => Effect34.sync(() => {
5475
+ return (name, type, status) => Effect35.sync(() => {
5235
5476
  const key = `${name}:${type}`;
5236
5477
  results.set(key, status);
5237
5478
  const line = ` ${name} ${c.dim(`(${type})`)} ${statusLabel(status)} ${formatDuration()}`;
@@ -5249,27 +5490,27 @@ var createLiveProgress = (manifest) => {
5249
5490
  });
5250
5491
  };
5251
5492
  var DEPLOY_CONCURRENCY = 5;
5252
- var prepareLayer = (input) => Effect34.gen(function* () {
5493
+ var prepareLayer = (input) => Effect35.gen(function* () {
5253
5494
  const layerResult = yield* ensureLayer({
5254
5495
  project: input.project,
5255
5496
  stage: input.stage,
5256
5497
  region: input.region,
5257
5498
  projectDir: input.packageDir
5258
5499
  }).pipe(
5259
- Effect34.provide(
5500
+ Effect35.provide(
5260
5501
  clients_exports.makeClients({
5261
5502
  lambda: { region: input.region }
5262
5503
  })
5263
5504
  )
5264
5505
  );
5265
5506
  const prodDeps = layerResult ? yield* readProductionDependencies(input.packageDir) : [];
5266
- const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect34.sync(() => collectLayerPackages(input.packageDir, prodDeps)) : { packages: [], warnings: [] };
5507
+ const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect35.sync(() => collectLayerPackages(input.packageDir, prodDeps)) : { packages: [], warnings: [] };
5267
5508
  for (const warning of layerWarnings) {
5268
- yield* Effect34.logWarning(`[layer] ${warning}`);
5509
+ yield* Effect35.logWarning(`[layer] ${warning}`);
5269
5510
  }
5270
- yield* Effect34.logDebug(`Layer result: ${layerResult ? "exists" : "null"}, external packages: ${external.length}`);
5511
+ yield* Effect35.logDebug(`Layer result: ${layerResult ? "exists" : "null"}, external packages: ${external.length}`);
5271
5512
  if (external.length > 0) {
5272
- yield* Effect34.logDebug(`Bundling with ${external.length} external packages from layer`);
5513
+ yield* Effect35.logDebug(`Bundling with ${external.length} external packages from layer`);
5273
5514
  }
5274
5515
  return {
5275
5516
  layerArn: layerResult?.layerVersionArn,
@@ -5425,7 +5666,7 @@ var buildTableTasks = (ctx, handlers, results) => {
5425
5666
  for (const { file, exports } of handlers) {
5426
5667
  for (const fn13 of exports) {
5427
5668
  tasks.push(
5428
- Effect34.gen(function* () {
5669
+ Effect35.gen(function* () {
5429
5670
  const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
5430
5671
  const result = yield* deployTableFunction({
5431
5672
  input: makeDeployInput(ctx, file),
@@ -5435,7 +5676,7 @@ var buildTableTasks = (ctx, handlers, results) => {
5435
5676
  depsEnv: env.depsEnv,
5436
5677
  depsPermissions: env.depsPermissions,
5437
5678
  ...fn13.staticGlobs.length > 0 ? { staticGlobs: fn13.staticGlobs } : {}
5438
- }).pipe(Effect34.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, dynamodb: { region } })));
5679
+ }).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, dynamodb: { region } })));
5439
5680
  results.push(result);
5440
5681
  yield* ctx.logComplete(fn13.exportName, "table", result.status);
5441
5682
  })
@@ -5450,7 +5691,7 @@ var buildAppTasks = (ctx, handlers, results, apiOriginDomain) => {
5450
5691
  for (const { exports } of handlers) {
5451
5692
  for (const fn13 of exports) {
5452
5693
  tasks.push(
5453
- Effect34.gen(function* () {
5694
+ Effect35.gen(function* () {
5454
5695
  const result = yield* deployApp({
5455
5696
  projectDir: ctx.input.projectDir,
5456
5697
  project: ctx.input.project,
@@ -5459,7 +5700,7 @@ var buildAppTasks = (ctx, handlers, results, apiOriginDomain) => {
5459
5700
  fn: fn13,
5460
5701
  verbose: ctx.input.verbose,
5461
5702
  ...apiOriginDomain ? { apiOriginDomain } : {}
5462
- }).pipe(Effect34.provide(clients_exports.makeClients({
5703
+ }).pipe(Effect35.provide(clients_exports.makeClients({
5463
5704
  lambda: { region },
5464
5705
  iam: { region },
5465
5706
  s3: { region },
@@ -5481,7 +5722,7 @@ var buildStaticSiteTasks = (ctx, handlers, results, apiOriginDomain) => {
5481
5722
  for (const { file, exports } of handlers) {
5482
5723
  for (const fn13 of exports) {
5483
5724
  tasks.push(
5484
- Effect34.gen(function* () {
5725
+ Effect35.gen(function* () {
5485
5726
  const result = yield* deployStaticSite({
5486
5727
  projectDir: ctx.input.projectDir,
5487
5728
  project: ctx.input.project,
@@ -5491,7 +5732,7 @@ var buildStaticSiteTasks = (ctx, handlers, results, apiOriginDomain) => {
5491
5732
  verbose: ctx.input.verbose,
5492
5733
  ...fn13.hasHandler ? { file } : {},
5493
5734
  ...apiOriginDomain ? { apiOriginDomain } : {}
5494
- }).pipe(Effect34.provide(clients_exports.makeClients({
5735
+ }).pipe(Effect35.provide(clients_exports.makeClients({
5495
5736
  s3: { region },
5496
5737
  cloudfront: { region: "us-east-1" },
5497
5738
  resource_groups_tagging_api: { region: "us-east-1" },
@@ -5511,7 +5752,7 @@ var buildFifoQueueTasks = (ctx, handlers, results) => {
5511
5752
  for (const { file, exports } of handlers) {
5512
5753
  for (const fn13 of exports) {
5513
5754
  tasks.push(
5514
- Effect34.gen(function* () {
5755
+ Effect35.gen(function* () {
5515
5756
  const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
5516
5757
  const result = yield* deployFifoQueueFunction({
5517
5758
  input: makeDeployInput(ctx, file),
@@ -5521,7 +5762,7 @@ var buildFifoQueueTasks = (ctx, handlers, results) => {
5521
5762
  depsEnv: env.depsEnv,
5522
5763
  depsPermissions: env.depsPermissions,
5523
5764
  ...fn13.staticGlobs.length > 0 ? { staticGlobs: fn13.staticGlobs } : {}
5524
- }).pipe(Effect34.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, sqs: { region } })));
5765
+ }).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, sqs: { region } })));
5525
5766
  results.push(result);
5526
5767
  yield* ctx.logComplete(fn13.exportName, "queue", result.status);
5527
5768
  })
@@ -5536,7 +5777,7 @@ var buildBucketTasks = (ctx, handlers, results) => {
5536
5777
  for (const { file, exports } of handlers) {
5537
5778
  for (const fn13 of exports) {
5538
5779
  tasks.push(
5539
- Effect34.gen(function* () {
5780
+ Effect35.gen(function* () {
5540
5781
  const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
5541
5782
  const result = yield* deployBucketFunction({
5542
5783
  input: makeDeployInput(ctx, file),
@@ -5546,7 +5787,7 @@ var buildBucketTasks = (ctx, handlers, results) => {
5546
5787
  depsEnv: env.depsEnv,
5547
5788
  depsPermissions: env.depsPermissions,
5548
5789
  ...fn13.staticGlobs.length > 0 ? { staticGlobs: fn13.staticGlobs } : {}
5549
- }).pipe(Effect34.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, s3: { region } })));
5790
+ }).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, s3: { region } })));
5550
5791
  results.push(result);
5551
5792
  const status = result.status === "resource-only" ? "created" : result.status;
5552
5793
  yield* ctx.logComplete(fn13.exportName, "bucket", status);
@@ -5562,13 +5803,13 @@ var buildMailerTasks = (ctx, handlers, results) => {
5562
5803
  for (const { exports } of handlers) {
5563
5804
  for (const fn13 of exports) {
5564
5805
  tasks.push(
5565
- Effect34.gen(function* () {
5806
+ Effect35.gen(function* () {
5566
5807
  const result = yield* deployMailer({
5567
5808
  project: ctx.input.project,
5568
5809
  stage: ctx.input.stage,
5569
5810
  region,
5570
5811
  fn: fn13
5571
- }).pipe(Effect34.provide(clients_exports.makeClients({ sesv2: { region } })));
5812
+ }).pipe(Effect35.provide(clients_exports.makeClients({ sesv2: { region } })));
5572
5813
  results.push(result);
5573
5814
  yield* ctx.logComplete(fn13.exportName, "mailer", result.verified ? "unchanged" : "created");
5574
5815
  })
@@ -5583,7 +5824,7 @@ var buildApiTasks = (ctx, handlers, results) => {
5583
5824
  for (const { file, exports } of handlers) {
5584
5825
  for (const fn13 of exports) {
5585
5826
  tasks.push(
5586
- Effect34.gen(function* () {
5827
+ Effect35.gen(function* () {
5587
5828
  const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
5588
5829
  const { exportName, functionArn, status, handlerName } = yield* deployApiFunction({
5589
5830
  input: makeDeployInput(ctx, file),
@@ -5593,13 +5834,13 @@ var buildApiTasks = (ctx, handlers, results) => {
5593
5834
  depsEnv: env.depsEnv,
5594
5835
  depsPermissions: env.depsPermissions,
5595
5836
  ...fn13.staticGlobs.length > 0 ? { staticGlobs: fn13.staticGlobs } : {}
5596
- }).pipe(Effect34.provide(clients_exports.makeClients({ lambda: { region }, iam: { region } })));
5837
+ }).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region } })));
5597
5838
  const lambdaName = `${ctx.input.project}-${ctx.stage}-${handlerName}`;
5598
5839
  const { functionUrl } = yield* ensureFunctionUrl(lambdaName).pipe(
5599
- Effect34.provide(clients_exports.makeClients({ lambda: { region } }))
5840
+ Effect35.provide(clients_exports.makeClients({ lambda: { region } }))
5600
5841
  );
5601
5842
  yield* addFunctionUrlPublicAccess(lambdaName).pipe(
5602
- Effect34.provide(clients_exports.makeClients({ lambda: { region } }))
5843
+ Effect35.provide(clients_exports.makeClients({ lambda: { region } }))
5603
5844
  );
5604
5845
  results.push({ exportName, url: functionUrl, functionArn });
5605
5846
  yield* ctx.logComplete(exportName, "api", status);
@@ -5609,13 +5850,13 @@ var buildApiTasks = (ctx, handlers, results) => {
5609
5850
  }
5610
5851
  return tasks;
5611
5852
  };
5612
- var deployProject = (input) => Effect34.gen(function* () {
5853
+ var deployProject = (input) => Effect35.gen(function* () {
5613
5854
  const stage = resolveStage(input.stage);
5614
5855
  const files = findHandlerFiles(input.patterns, input.projectDir);
5615
5856
  if (files.length === 0) {
5616
- return yield* Effect34.fail(new Error(`No files match patterns: ${input.patterns.join(", ")}`));
5857
+ return yield* Effect35.fail(new Error(`No files match patterns: ${input.patterns.join(", ")}`));
5617
5858
  }
5618
- yield* Effect34.logDebug(`Found ${files.length} file(s) matching patterns`);
5859
+ yield* Effect35.logDebug(`Found ${files.length} file(s) matching patterns`);
5619
5860
  const { tableHandlers, appHandlers, staticSiteHandlers, fifoQueueHandlers, bucketHandlers, mailerHandlers, apiHandlers } = discoverHandlers(files);
5620
5861
  const totalTableHandlers = tableHandlers.reduce((acc, h) => acc + h.exports.length, 0);
5621
5862
  const totalAppHandlers = appHandlers.reduce((acc, h) => acc + h.exports.length, 0);
@@ -5626,7 +5867,7 @@ var deployProject = (input) => Effect34.gen(function* () {
5626
5867
  const totalApiHandlers = apiHandlers.reduce((acc, h) => acc + h.exports.length, 0);
5627
5868
  const totalAllHandlers = totalTableHandlers + totalAppHandlers + totalStaticSiteHandlers + totalFifoQueueHandlers + totalBucketHandlers + totalMailerHandlers + totalApiHandlers;
5628
5869
  if (totalAllHandlers === 0) {
5629
- return yield* Effect34.fail(new Error("No handlers found in matched files"));
5870
+ return yield* Effect35.fail(new Error("No handlers found in matched files"));
5630
5871
  }
5631
5872
  const parts = [];
5632
5873
  if (totalTableHandlers > 0) parts.push(`${totalTableHandlers} table`);
@@ -5642,7 +5883,7 @@ var deployProject = (input) => Effect34.gen(function* () {
5642
5883
  const requiredParams = collectRequiredParams(discovered, input.project, stage);
5643
5884
  if (requiredParams.length > 0) {
5644
5885
  const { missing } = yield* checkMissingParams(requiredParams).pipe(
5645
- Effect34.provide(clients_exports.makeClients({ ssm: { region: input.region } }))
5886
+ Effect35.provide(clients_exports.makeClients({ ssm: { region: input.region } }))
5646
5887
  );
5647
5888
  if (missing.length > 0) {
5648
5889
  yield* Console2.log(`
@@ -5664,14 +5905,15 @@ var deployProject = (input) => Effect34.gen(function* () {
5664
5905
  for (const err of depsErrors) {
5665
5906
  yield* Console2.log(` ${c.red("\u2717")} ${err}`);
5666
5907
  }
5667
- return yield* Effect34.fail(new Error("Unresolved deps \u2014 aborting deploy"));
5908
+ return yield* Effect35.fail(new Error("Unresolved deps \u2014 aborting deploy"));
5668
5909
  }
5669
- const { layerArn, layerVersion, layerStatus, external } = yield* prepareLayer({
5910
+ const needsLambda = totalTableHandlers + totalAppHandlers + totalFifoQueueHandlers + totalBucketHandlers + totalMailerHandlers + totalApiHandlers > 0;
5911
+ const { layerArn, layerVersion, layerStatus, external } = needsLambda ? yield* prepareLayer({
5670
5912
  project: input.project,
5671
5913
  stage,
5672
5914
  region: input.region,
5673
5915
  packageDir: input.packageDir ?? input.projectDir
5674
- });
5916
+ }) : { layerArn: void 0, layerVersion: void 0, layerStatus: void 0, external: [] };
5675
5917
  if (layerArn && layerStatus) {
5676
5918
  const status = layerStatus === "cached" ? c.dim("cached") : c.green("created");
5677
5919
  yield* Console2.log(` ${c.dim("Layer:")} ${status} ${c.dim(`v${layerVersion}`)} (${external.length} packages)`);
@@ -5728,7 +5970,7 @@ var deployProject = (input) => Effect34.gen(function* () {
5728
5970
  ...buildBucketTasks(ctx, bucketHandlers, bucketResults),
5729
5971
  ...buildMailerTasks(ctx, mailerHandlers, mailerResults)
5730
5972
  ];
5731
- yield* Effect34.all(phase1Tasks, { concurrency: DEPLOY_CONCURRENCY, discard: true });
5973
+ yield* Effect35.all(phase1Tasks, { concurrency: DEPLOY_CONCURRENCY, discard: true });
5732
5974
  const firstApiUrl = apiResults[0]?.url;
5733
5975
  const apiOriginDomain = firstApiUrl ? firstApiUrl.replace("https://", "").replace(/\/$/, "") : void 0;
5734
5976
  const phase2Tasks = [
@@ -5736,7 +5978,7 @@ var deployProject = (input) => Effect34.gen(function* () {
5736
5978
  ...input.noSites ? [] : buildStaticSiteTasks(ctx, staticSiteHandlers, staticSiteResults, apiOriginDomain)
5737
5979
  ];
5738
5980
  if (phase2Tasks.length > 0) {
5739
- yield* Effect34.all(phase2Tasks, { concurrency: DEPLOY_CONCURRENCY, discard: true });
5981
+ yield* Effect35.all(phase2Tasks, { concurrency: DEPLOY_CONCURRENCY, discard: true });
5740
5982
  }
5741
5983
  } else {
5742
5984
  const tasks = [
@@ -5748,16 +5990,16 @@ var deployProject = (input) => Effect34.gen(function* () {
5748
5990
  ...buildBucketTasks(ctx, bucketHandlers, bucketResults),
5749
5991
  ...buildMailerTasks(ctx, mailerHandlers, mailerResults)
5750
5992
  ];
5751
- yield* Effect34.all(tasks, { concurrency: DEPLOY_CONCURRENCY, discard: true });
5993
+ yield* Effect35.all(tasks, { concurrency: DEPLOY_CONCURRENCY, discard: true });
5752
5994
  }
5753
5995
  if (!input.noSites && staticSiteResults.length > 0 || appResults.length > 0) {
5754
5996
  yield* cleanupOrphanedFunctions(input.project, stage).pipe(
5755
- Effect34.provide(clients_exports.makeClients({
5997
+ Effect35.provide(clients_exports.makeClients({
5756
5998
  cloudfront: { region: "us-east-1" },
5757
5999
  resource_groups_tagging_api: { region: "us-east-1" }
5758
6000
  })),
5759
- Effect34.catchAll(
5760
- (error) => Effect34.logDebug(`CloudFront Function cleanup failed (non-fatal): ${error}`)
6001
+ Effect35.catchAll(
6002
+ (error) => Effect35.logDebug(`CloudFront Function cleanup failed (non-fatal): ${error}`)
5761
6003
  )
5762
6004
  );
5763
6005
  }
@@ -5766,17 +6008,17 @@ var deployProject = (input) => Effect34.gen(function* () {
5766
6008
 
5767
6009
  // src/cli/config.ts
5768
6010
  import { Options } from "@effect/cli";
5769
- import * as path7 from "path";
5770
- import * as fs4 from "fs";
6011
+ import * as path8 from "path";
6012
+ import * as fs6 from "fs";
5771
6013
  import { pathToFileURL } from "url";
5772
6014
  import * as esbuild2 from "esbuild";
5773
- import { Effect as Effect35 } from "effect";
5774
- var loadConfig = Effect35.fn("loadConfig")(function* () {
5775
- const configPath = path7.resolve(process.cwd(), "effortless.config.ts");
5776
- if (!fs4.existsSync(configPath)) {
6015
+ import { Effect as Effect36 } from "effect";
6016
+ var loadConfig = Effect36.fn("loadConfig")(function* () {
6017
+ const configPath = path8.resolve(process.cwd(), "effortless.config.ts");
6018
+ if (!fs6.existsSync(configPath)) {
5777
6019
  return null;
5778
6020
  }
5779
- const result = yield* Effect35.tryPromise({
6021
+ const result = yield* Effect36.tryPromise({
5780
6022
  try: () => esbuild2.build({
5781
6023
  entryPoints: [configPath],
5782
6024
  bundle: true,
@@ -5792,12 +6034,12 @@ var loadConfig = Effect35.fn("loadConfig")(function* () {
5792
6034
  return null;
5793
6035
  }
5794
6036
  const code = output.text;
5795
- const tempFile = path7.join(process.cwd(), ".effortless-config.mjs");
5796
- fs4.writeFileSync(tempFile, code);
5797
- const mod = yield* Effect35.tryPromise({
6037
+ const tempFile = path8.join(process.cwd(), ".effortless-config.mjs");
6038
+ fs6.writeFileSync(tempFile, code);
6039
+ const mod = yield* Effect36.tryPromise({
5798
6040
  try: () => import(pathToFileURL(tempFile).href),
5799
6041
  catch: (error) => new Error(`Failed to load config: ${error}`)
5800
- }).pipe(Effect35.ensuring(Effect35.sync(() => fs4.unlinkSync(tempFile))));
6042
+ }).pipe(Effect36.ensuring(Effect36.sync(() => fs6.unlinkSync(tempFile))));
5801
6043
  return mod.default;
5802
6044
  });
5803
6045
  var projectOption = Options.text("project").pipe(
@@ -5848,15 +6090,15 @@ var getPatternsFromConfig = (config) => {
5848
6090
  // src/cli/project-config.ts
5849
6091
  import * as Context13 from "effect/Context";
5850
6092
  import * as Layer14 from "effect/Layer";
5851
- import * as Effect36 from "effect/Effect";
5852
- import * as path8 from "path";
6093
+ import * as Effect37 from "effect/Effect";
6094
+ import * as path9 from "path";
5853
6095
  var ProjectConfig = class _ProjectConfig extends Context13.Tag("ProjectConfig")() {
5854
6096
  static Live = Layer14.effect(
5855
6097
  _ProjectConfig,
5856
- Effect36.gen(function* () {
6098
+ Effect37.gen(function* () {
5857
6099
  const config = yield* loadConfig();
5858
6100
  const cwd = process.cwd();
5859
- const projectDir = config?.root ? path8.resolve(cwd, config.root) : cwd;
6101
+ const projectDir = config?.root ? path9.resolve(cwd, config.root) : cwd;
5860
6102
  return { config, cwd, projectDir };
5861
6103
  })
5862
6104
  );
@@ -5873,7 +6115,7 @@ var isFilePath = (target) => {
5873
6115
  var deployCommand = Command.make(
5874
6116
  "deploy",
5875
6117
  { target: deployTargetArg, project: projectOption, stage: stageOption, region: regionOption, verbose: verboseOption, noSites: noSitesOption },
5876
- ({ target, project: projectOpt, stage, region, verbose, noSites }) => Effect37.gen(function* () {
6118
+ ({ target, project: projectOpt, stage, region, verbose, noSites }) => Effect38.gen(function* () {
5877
6119
  const { config, cwd, projectDir } = yield* ProjectConfig;
5878
6120
  const project = Option.getOrElse(projectOpt, () => config?.name ?? "");
5879
6121
  const finalStage = config?.stage ?? stage;
@@ -5893,7 +6135,7 @@ var deployCommand = Command.make(
5893
6135
  });
5894
6136
  const logLevel = verbose ? LogLevel.Debug : LogLevel.Warning;
5895
6137
  yield* Option.match(target, {
5896
- onNone: () => Effect37.gen(function* () {
6138
+ onNone: () => Effect38.gen(function* () {
5897
6139
  const patterns = getPatternsFromConfig(config);
5898
6140
  if (!patterns) {
5899
6141
  yield* Console3.error("Error: No target specified and no 'handlers' patterns in config");
@@ -5923,16 +6165,30 @@ ${c.green(`Deployed ${total} handler(s):`)}`);
5923
6165
  summaryLines.push({ name: r.exportName, line: ` ${c.cyan("[api]")} ${c.bold(r.exportName)} ${c.dim(r.url)}` });
5924
6166
  }
5925
6167
  for (const r of results.staticSiteResults) {
5926
- summaryLines.push({ name: r.exportName, line: ` ${c.cyan("[site]")} ${c.bold(r.exportName)}: ${c.cyan(r.url)}` });
6168
+ let line = ` ${c.cyan("[site]")} ${c.bold(r.exportName)}: ${c.cyan(r.url)}`;
6169
+ const extras = [];
6170
+ if (r.seoGenerated) extras.push(`seo: ${r.seoGenerated.join(", ")}`);
6171
+ if (r.indexingResult) {
6172
+ const { submitted, skipped, failed } = r.indexingResult;
6173
+ if (submitted > 0 || failed > 0) {
6174
+ const parts = [`${submitted} submitted`];
6175
+ if (failed > 0) parts.push(c.red(`${failed} failed`));
6176
+ extras.push(`indexing: ${parts.join(", ")}`);
6177
+ } else {
6178
+ extras.push(`indexing: all ${skipped} pages already indexed`);
6179
+ }
6180
+ }
6181
+ if (extras.length > 0) line += ` ${c.dim(extras.join(" | "))}`;
6182
+ summaryLines.push({ name: r.exportName, line });
5927
6183
  }
5928
6184
  summaryLines.sort((a, b) => a.name.localeCompare(b.name));
5929
6185
  for (const { line } of summaryLines) {
5930
6186
  yield* Console3.log(line);
5931
6187
  }
5932
6188
  }),
5933
- onSome: (targetValue) => Effect37.gen(function* () {
6189
+ onSome: (targetValue) => Effect38.gen(function* () {
5934
6190
  if (isFilePath(targetValue)) {
5935
- const fullPath = path9.isAbsolute(targetValue) ? targetValue : path9.resolve(projectDir, targetValue);
6191
+ const fullPath = path10.isAbsolute(targetValue) ? targetValue : path10.resolve(projectDir, targetValue);
5936
6192
  const input = {
5937
6193
  projectDir,
5938
6194
  packageDir: cwd,
@@ -5942,9 +6198,9 @@ ${c.green(`Deployed ${total} handler(s):`)}`);
5942
6198
  region: finalRegion
5943
6199
  };
5944
6200
  const tableResults = yield* deployAllTables(input).pipe(
5945
- Effect37.catchIf(
6201
+ Effect38.catchIf(
5946
6202
  (e) => e instanceof Error && e.message.includes("No defineTable"),
5947
- () => Effect37.succeed([])
6203
+ () => Effect38.succeed([])
5948
6204
  )
5949
6205
  );
5950
6206
  if (tableResults.length === 0) {
@@ -5979,7 +6235,7 @@ Deployed ${tableResults.length} table handler(s):`));
5979
6235
  const foundFile = found.file;
5980
6236
  const foundExport = found.exportName;
5981
6237
  const handlerType = found.type;
5982
- yield* Console3.log(`Found handler ${c.bold(targetValue)} in ${c.dim(path9.relative(projectDir, foundFile))}`);
6238
+ yield* Console3.log(`Found handler ${c.bold(targetValue)} in ${c.dim(path10.relative(projectDir, foundFile))}`);
5983
6239
  const input = {
5984
6240
  projectDir,
5985
6241
  packageDir: cwd,
@@ -6001,15 +6257,15 @@ ${c.green("Deployed:")} ${c.cyan(result.url)}`);
6001
6257
  }
6002
6258
  })
6003
6259
  }).pipe(
6004
- Effect37.provide(clientsLayer),
6260
+ Effect38.provide(clientsLayer),
6005
6261
  Logger.withMinimumLogLevel(logLevel)
6006
6262
  );
6007
- }).pipe(Effect37.provide(ProjectConfig.Live))
6263
+ }).pipe(Effect38.provide(ProjectConfig.Live))
6008
6264
  ).pipe(Command.withDescription("Deploy handlers to AWS Lambda. Accepts a handler name, file path, or deploys all from config"));
6009
6265
 
6010
6266
  // src/cli/commands/status.ts
6011
6267
  import { Command as Command2 } from "@effect/cli";
6012
- import { Effect as Effect38, Console as Console4, Logger as Logger2, LogLevel as LogLevel2, Option as Option2 } from "effect";
6268
+ import { Effect as Effect39, Console as Console4, Logger as Logger2, LogLevel as LogLevel2, Option as Option2 } from "effect";
6013
6269
  var { lambda } = clients_exports;
6014
6270
  var INTERNAL_HANDLERS = /* @__PURE__ */ new Set(["api", "platform"]);
6015
6271
  var extractFunctionName = (arn) => {
@@ -6030,7 +6286,7 @@ var formatDate = (date) => {
6030
6286
  if (days < 7) return `${days}d ago`;
6031
6287
  return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
6032
6288
  };
6033
- var getLambdaDetails = (functionName) => Effect38.gen(function* () {
6289
+ var getLambdaDetails = (functionName) => Effect39.gen(function* () {
6034
6290
  const config = yield* lambda.make("get_function_configuration", {
6035
6291
  FunctionName: functionName
6036
6292
  });
@@ -6040,7 +6296,7 @@ var getLambdaDetails = (functionName) => Effect38.gen(function* () {
6040
6296
  timeout: config.Timeout
6041
6297
  };
6042
6298
  }).pipe(
6043
- Effect38.catchAll(() => Effect38.succeed({}))
6299
+ Effect39.catchAll(() => Effect39.succeed({}))
6044
6300
  );
6045
6301
  var discoverCodeHandlers = (projectDir, patterns) => {
6046
6302
  const files = findHandlerFiles(patterns, projectDir);
@@ -6092,9 +6348,9 @@ var STATUS_COLORS = {
6092
6348
  var formatStatus = (status) => {
6093
6349
  return STATUS_COLORS[status](status.padEnd(10));
6094
6350
  };
6095
- var formatRoute = (method, path11) => {
6096
- if (method && path11) return `${method.padEnd(5)} ${path11}`;
6097
- if (path11) return path11;
6351
+ var formatRoute = (method, path12) => {
6352
+ if (method && path12) return `${method.padEnd(5)} ${path12}`;
6353
+ if (path12) return path12;
6098
6354
  return "";
6099
6355
  };
6100
6356
  var formatEntry = (entry) => {
@@ -6116,7 +6372,7 @@ var formatEntry = (entry) => {
6116
6372
  var statusCommand = Command2.make(
6117
6373
  "status",
6118
6374
  { project: projectOption, stage: stageOption, region: regionOption, verbose: verboseOption },
6119
- ({ project: projectOpt, stage, region, verbose }) => Effect38.gen(function* () {
6375
+ ({ project: projectOpt, stage, region, verbose }) => Effect39.gen(function* () {
6120
6376
  const { config, projectDir } = yield* ProjectConfig;
6121
6377
  const project = Option2.getOrElse(projectOpt, () => config?.name ?? "");
6122
6378
  const finalStage = config?.stage ?? stage;
@@ -6133,7 +6389,7 @@ var statusCommand = Command2.make(
6133
6389
  const patterns = getPatternsFromConfig(config);
6134
6390
  const codeHandlers = patterns ? discoverCodeHandlers(projectDir, patterns) : [];
6135
6391
  const codeHandlerNames = new Set(codeHandlers.map((h) => h.name));
6136
- yield* Effect38.gen(function* () {
6392
+ yield* Effect39.gen(function* () {
6137
6393
  yield* Console4.log(`
6138
6394
  Status for ${c.bold(project + "/" + finalStage)}:
6139
6395
  `);
@@ -6207,7 +6463,7 @@ Status for ${c.bold(project + "/" + finalStage)}:
6207
6463
  yield* Console4.log(`
6208
6464
  Total: ${parts.join(", ")}`);
6209
6465
  const depWarnings = yield* checkDependencyWarnings(projectDir).pipe(
6210
- Effect38.catchAll(() => Effect38.succeed([]))
6466
+ Effect39.catchAll(() => Effect39.succeed([]))
6211
6467
  );
6212
6468
  if (depWarnings.length > 0) {
6213
6469
  yield* Console4.log("");
@@ -6216,18 +6472,18 @@ Total: ${parts.join(", ")}`);
6216
6472
  }
6217
6473
  }
6218
6474
  }).pipe(
6219
- Effect38.provide(clientsLayer),
6475
+ Effect39.provide(clientsLayer),
6220
6476
  Logger2.withMinimumLogLevel(logLevel)
6221
6477
  );
6222
- }).pipe(Effect38.provide(ProjectConfig.Live))
6478
+ }).pipe(Effect39.provide(ProjectConfig.Live))
6223
6479
  ).pipe(Command2.withDescription("Compare local handlers with deployed AWS resources. Shows new, deployed, and orphaned handlers"));
6224
6480
 
6225
6481
  // src/cli/commands/cleanup.ts
6226
6482
  import { Command as Command3, Options as Options2 } from "@effect/cli";
6227
- import { Effect as Effect40, Console as Console5, Logger as Logger3, LogLevel as LogLevel3, Option as Option3 } from "effect";
6483
+ import { Effect as Effect41, Console as Console5, Logger as Logger3, LogLevel as LogLevel3, Option as Option3 } from "effect";
6228
6484
 
6229
6485
  // src/deploy/cleanup.ts
6230
- import { Effect as Effect39 } from "effect";
6486
+ import { Effect as Effect40 } from "effect";
6231
6487
  var extractResourceName = (arn, type) => {
6232
6488
  switch (type) {
6233
6489
  case "lambda": {
@@ -6270,7 +6526,7 @@ var extractLayerInfo = (arn) => {
6270
6526
  version: parseInt(parts[parts.length - 1] ?? "0", 10)
6271
6527
  };
6272
6528
  };
6273
- var deleteResource = (resource) => Effect39.gen(function* () {
6529
+ var deleteResource = (resource) => Effect40.gen(function* () {
6274
6530
  const name = extractResourceName(resource.arn, resource.type);
6275
6531
  switch (resource.type) {
6276
6532
  case "lambda":
@@ -6303,18 +6559,18 @@ var deleteResource = (resource) => Effect39.gen(function* () {
6303
6559
  yield* deleteSesIdentity(name);
6304
6560
  break;
6305
6561
  default:
6306
- yield* Effect39.logWarning(`Unknown resource type: ${resource.type}, skipping ${resource.arn}`);
6562
+ yield* Effect40.logWarning(`Unknown resource type: ${resource.type}, skipping ${resource.arn}`);
6307
6563
  }
6308
6564
  });
6309
- var deleteResources = (resources) => Effect39.gen(function* () {
6565
+ var deleteResources = (resources) => Effect40.gen(function* () {
6310
6566
  const orderedTypes = ["lambda", "api-gateway", "cloudfront-distribution", "sqs", "ses", "dynamodb", "s3-bucket", "lambda-layer", "iam-role"];
6311
6567
  const iamRolesToDelete = /* @__PURE__ */ new Set();
6312
6568
  for (const type of orderedTypes) {
6313
6569
  const resourcesOfType = resources.filter((r) => r.type === type);
6314
6570
  for (const resource of resourcesOfType) {
6315
6571
  yield* deleteResource(resource).pipe(
6316
- Effect39.catchAll(
6317
- (error) => Effect39.logError(`Failed to delete ${resource.type} ${resource.arn}: ${error}`)
6572
+ Effect40.catchAll(
6573
+ (error) => Effect40.logError(`Failed to delete ${resource.type} ${resource.arn}: ${error}`)
6318
6574
  )
6319
6575
  );
6320
6576
  if (resource.type === "lambda") {
@@ -6326,8 +6582,8 @@ var deleteResources = (resources) => Effect39.gen(function* () {
6326
6582
  }
6327
6583
  for (const roleName of iamRolesToDelete) {
6328
6584
  yield* deleteRole(roleName).pipe(
6329
- Effect39.catchAll(
6330
- (error) => Effect39.logError(`Failed to delete IAM role ${roleName}: ${error}`)
6585
+ Effect40.catchAll(
6586
+ (error) => Effect40.logError(`Failed to delete IAM role ${roleName}: ${error}`)
6331
6587
  )
6332
6588
  );
6333
6589
  }
@@ -6354,7 +6610,7 @@ var orphanedOption = Options2.boolean("orphaned").pipe(
6354
6610
  var cleanupCommand = Command3.make(
6355
6611
  "cleanup",
6356
6612
  { project: projectOption, stage: stageOption, region: regionOption, handler: handlerOption, layer: layerOption, roles: rolesOption, orphaned: orphanedOption, all: cleanupAllOption, dryRun: dryRunOption, verbose: verboseOption },
6357
- ({ project: projectOpt, stage, region, handler: handlerOpt, layer: cleanupLayer, roles: cleanupRoles, orphaned: cleanupOrphaned, all: deleteAll, dryRun, verbose }) => Effect40.gen(function* () {
6613
+ ({ project: projectOpt, stage, region, handler: handlerOpt, layer: cleanupLayer, roles: cleanupRoles, orphaned: cleanupOrphaned, all: deleteAll, dryRun, verbose }) => Effect41.gen(function* () {
6358
6614
  const { config, projectDir } = yield* ProjectConfig;
6359
6615
  const project = Option3.getOrElse(projectOpt, () => config?.name ?? "");
6360
6616
  const finalStage = config?.stage ?? stage;
@@ -6366,14 +6622,14 @@ var cleanupCommand = Command3.make(
6366
6622
  const logLevel = verbose ? LogLevel3.Debug : LogLevel3.Info;
6367
6623
  if (cleanupLayer) {
6368
6624
  yield* cleanupLayerVersions({ project, region: finalRegion, deleteAll, dryRun }).pipe(
6369
- Effect40.provide(clients_exports.makeClients({ lambda: { region: finalRegion } })),
6625
+ Effect41.provide(clients_exports.makeClients({ lambda: { region: finalRegion } })),
6370
6626
  Logger3.withMinimumLogLevel(logLevel)
6371
6627
  );
6372
6628
  return;
6373
6629
  }
6374
6630
  if (cleanupRoles) {
6375
6631
  yield* cleanupIamRoles({ project, stage: finalStage, region: finalRegion, deleteAll, dryRun }).pipe(
6376
- Effect40.provide(clients_exports.makeClients({ iam: { region: finalRegion } })),
6632
+ Effect41.provide(clients_exports.makeClients({ iam: { region: finalRegion } })),
6377
6633
  Logger3.withMinimumLogLevel(logLevel)
6378
6634
  );
6379
6635
  return;
@@ -6388,7 +6644,7 @@ var cleanupCommand = Command3.make(
6388
6644
  s3: { region: finalRegion },
6389
6645
  cloudfront: { region: "us-east-1" }
6390
6646
  });
6391
- yield* Effect40.gen(function* () {
6647
+ yield* Effect41.gen(function* () {
6392
6648
  yield* Console5.log(`
6393
6649
  Looking for resources in ${c.bold(project + "/" + finalStage)}...
6394
6650
  `);
@@ -6468,12 +6724,12 @@ ${c.yellow("[DRY RUN]")} No resources were deleted.`);
6468
6724
  yield* deleteResources(resourcesToDelete);
6469
6725
  yield* Console5.log(c.green("\nDone!"));
6470
6726
  }).pipe(
6471
- Effect40.provide(clientsLayer),
6727
+ Effect41.provide(clientsLayer),
6472
6728
  Logger3.withMinimumLogLevel(logLevel)
6473
6729
  );
6474
- }).pipe(Effect40.provide(ProjectConfig.Live))
6730
+ }).pipe(Effect41.provide(ProjectConfig.Live))
6475
6731
  ).pipe(Command3.withDescription("Delete deployed resources (Lambda, API Gateway, DynamoDB, IAM roles, layers)"));
6476
- var cleanupLayerVersions = (input) => Effect40.gen(function* () {
6732
+ var cleanupLayerVersions = (input) => Effect41.gen(function* () {
6477
6733
  const layerName = `${input.project}-deps`;
6478
6734
  yield* Console5.log(`
6479
6735
  Searching for layer versions: ${layerName}
@@ -6505,7 +6761,7 @@ ${c.yellow("[DRY RUN]")} No layers were deleted.`);
6505
6761
  yield* Console5.log(c.green(`
6506
6762
  Deleted ${deleted} layer version(s).`));
6507
6763
  });
6508
- var cleanupIamRoles = (input) => Effect40.gen(function* () {
6764
+ var cleanupIamRoles = (input) => Effect41.gen(function* () {
6509
6765
  yield* Console5.log("\nSearching for effortless IAM roles...\n");
6510
6766
  const allRoles = yield* listEffortlessRoles();
6511
6767
  if (allRoles.length === 0) {
@@ -6554,8 +6810,8 @@ ${c.yellow("[DRY RUN]")} No roles were deleted.`);
6554
6810
  yield* Console5.log(c.red("\nDeleting roles..."));
6555
6811
  for (const role of roles) {
6556
6812
  yield* deleteRole(role.name).pipe(
6557
- Effect40.catchAll(
6558
- (error) => Effect40.logError(`Failed to delete ${role.name}: ${error}`)
6813
+ Effect41.catchAll(
6814
+ (error) => Effect41.logError(`Failed to delete ${role.name}: ${error}`)
6559
6815
  )
6560
6816
  );
6561
6817
  }
@@ -6564,7 +6820,7 @@ ${c.yellow("[DRY RUN]")} No roles were deleted.`);
6564
6820
 
6565
6821
  // src/cli/commands/logs.ts
6566
6822
  import { Args as Args2, Command as Command4, Options as Options3 } from "@effect/cli";
6567
- import { Effect as Effect41, Console as Console6, Logger as Logger4, LogLevel as LogLevel4, Option as Option4, Schedule as Schedule4 } from "effect";
6823
+ import { Effect as Effect42, Console as Console6, Logger as Logger4, LogLevel as LogLevel4, Option as Option4, Schedule as Schedule4 } from "effect";
6568
6824
  var { cloudwatch_logs } = clients_exports;
6569
6825
  var handlerArg = Args2.text({ name: "handler" }).pipe(
6570
6826
  Args2.withDescription("Handler name to show logs for")
@@ -6635,7 +6891,7 @@ var fetchLogs = (logGroupName, startTime, nextToken) => cloudwatch_logs.make("fi
6635
6891
  var logsCommand = Command4.make(
6636
6892
  "logs",
6637
6893
  { handler: handlerArg, project: projectOption, stage: stageOption, region: regionOption, tail: tailOption, since: sinceOption, verbose: verboseOption },
6638
- ({ handler: handlerName, project: projectOpt, stage, region, tail, since, verbose }) => Effect41.gen(function* () {
6894
+ ({ handler: handlerName, project: projectOpt, stage, region, tail, since, verbose }) => Effect42.gen(function* () {
6639
6895
  const { config, projectDir } = yield* ProjectConfig;
6640
6896
  const project = Option4.getOrElse(projectOpt, () => config?.name ?? "");
6641
6897
  const finalStage = config?.stage ?? stage;
@@ -6666,18 +6922,18 @@ var logsCommand = Command4.make(
6666
6922
  cloudwatch_logs: { region: finalRegion }
6667
6923
  });
6668
6924
  const logLevel = verbose ? LogLevel4.Debug : LogLevel4.Info;
6669
- yield* Effect41.gen(function* () {
6925
+ yield* Effect42.gen(function* () {
6670
6926
  const durationMs = parseDuration(since);
6671
6927
  let startTime = Date.now() - durationMs;
6672
6928
  yield* Console6.log(`Logs for ${c.bold(handlerName)} ${c.dim(`(${logGroupName})`)}:
6673
6929
  `);
6674
6930
  let hasLogs = false;
6675
6931
  const result = yield* fetchLogs(logGroupName, startTime).pipe(
6676
- Effect41.catchAll((error) => {
6932
+ Effect42.catchAll((error) => {
6677
6933
  if (error instanceof clients_exports.cloudwatch_logs.CloudWatchLogsError && error.cause.name === "ResourceNotFoundException") {
6678
- return Effect41.succeed({ events: void 0, nextToken: void 0 });
6934
+ return Effect42.succeed({ events: void 0, nextToken: void 0 });
6679
6935
  }
6680
- return Effect41.fail(error);
6936
+ return Effect42.fail(error);
6681
6937
  })
6682
6938
  );
6683
6939
  if (result.events && result.events.length > 0) {
@@ -6701,10 +6957,10 @@ var logsCommand = Command4.make(
6701
6957
  if (!hasLogs) {
6702
6958
  yield* Console6.log("Waiting for logs... (Ctrl+C to stop)\n");
6703
6959
  }
6704
- yield* Effect41.repeat(
6705
- Effect41.gen(function* () {
6960
+ yield* Effect42.repeat(
6961
+ Effect42.gen(function* () {
6706
6962
  const result2 = yield* fetchLogs(logGroupName, startTime).pipe(
6707
- Effect41.catchAll(() => Effect41.succeed({ events: void 0, nextToken: void 0 }))
6963
+ Effect42.catchAll(() => Effect42.succeed({ events: void 0, nextToken: void 0 }))
6708
6964
  );
6709
6965
  if (result2.events && result2.events.length > 0) {
6710
6966
  for (const event of result2.events) {
@@ -6722,45 +6978,45 @@ var logsCommand = Command4.make(
6722
6978
  Schedule4.spaced("2 seconds")
6723
6979
  );
6724
6980
  }).pipe(
6725
- Effect41.provide(clientsLayer),
6981
+ Effect42.provide(clientsLayer),
6726
6982
  Logger4.withMinimumLogLevel(logLevel)
6727
6983
  );
6728
- }).pipe(Effect41.provide(ProjectConfig.Live))
6984
+ }).pipe(Effect42.provide(ProjectConfig.Live))
6729
6985
  ).pipe(Command4.withDescription("Stream CloudWatch logs for a handler. Supports --tail for live tailing and --since for time range"));
6730
6986
 
6731
6987
  // src/cli/commands/layer.ts
6732
6988
  import { Command as Command5, Options as Options4 } from "@effect/cli";
6733
- import { Effect as Effect42, Console as Console7 } from "effect";
6734
- import * as path10 from "path";
6735
- import * as fs5 from "fs";
6989
+ import { Effect as Effect43, Console as Console7 } from "effect";
6990
+ import * as path11 from "path";
6991
+ import * as fs7 from "fs";
6736
6992
  var buildOption = Options4.boolean("build").pipe(
6737
6993
  Options4.withDescription("Build layer directory locally (for debugging)")
6738
6994
  );
6739
6995
  var layerCommand = Command5.make(
6740
6996
  "layer",
6741
6997
  { build: buildOption, output: outputOption, verbose: verboseOption },
6742
- ({ build: build3, output, verbose }) => Effect42.gen(function* () {
6998
+ ({ build: build3, output, verbose }) => Effect43.gen(function* () {
6743
6999
  const { config, cwd } = yield* ProjectConfig;
6744
7000
  if (build3) {
6745
7001
  yield* buildLayer(cwd, output, verbose);
6746
7002
  } else {
6747
7003
  yield* showLayerInfo(cwd, config?.name, verbose);
6748
7004
  }
6749
- }).pipe(Effect42.provide(ProjectConfig.Live))
7005
+ }).pipe(Effect43.provide(ProjectConfig.Live))
6750
7006
  ).pipe(Command5.withDescription("Inspect or locally build the shared Lambda dependency layer from package.json"));
6751
- var showLayerInfo = (projectDir, projectName, verbose) => Effect42.gen(function* () {
7007
+ var showLayerInfo = (projectDir, projectName, verbose) => Effect43.gen(function* () {
6752
7008
  yield* Console7.log(`
6753
7009
  ${c.bold("=== Layer Packages Preview ===")}
6754
7010
  `);
6755
7011
  const depWarnings = yield* checkDependencyWarnings(projectDir).pipe(
6756
- Effect42.catchAll(() => Effect42.succeed([]))
7012
+ Effect43.catchAll(() => Effect43.succeed([]))
6757
7013
  );
6758
7014
  for (const w of depWarnings) {
6759
7015
  yield* Console7.log(c.yellow(` \u26A0 ${w}`));
6760
7016
  }
6761
7017
  if (depWarnings.length > 0) yield* Console7.log("");
6762
7018
  const prodDeps = yield* readProductionDependencies(projectDir).pipe(
6763
- Effect42.catchAll(() => Effect42.succeed([]))
7019
+ Effect43.catchAll(() => Effect43.succeed([]))
6764
7020
  );
6765
7021
  if (prodDeps.length === 0) {
6766
7022
  yield* Console7.log("No production dependencies found in package.json");
@@ -6772,7 +7028,7 @@ ${c.bold("=== Layer Packages Preview ===")}
6772
7028
  yield* Console7.log(` ${dep}`);
6773
7029
  }
6774
7030
  const hash = yield* computeLockfileHash(projectDir).pipe(
6775
- Effect42.catchAll(() => Effect42.succeed(null))
7031
+ Effect43.catchAll(() => Effect43.succeed(null))
6776
7032
  );
6777
7033
  if (hash) {
6778
7034
  yield* Console7.log(`
@@ -6780,7 +7036,7 @@ Lockfile hash: ${hash}`);
6780
7036
  } else {
6781
7037
  yield* Console7.log("\nNo lockfile found (package-lock.json, pnpm-lock.yaml, or yarn.lock)");
6782
7038
  }
6783
- const { packages: allPackages, warnings: layerWarnings } = yield* Effect42.sync(() => collectLayerPackages(projectDir, prodDeps));
7039
+ const { packages: allPackages, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps));
6784
7040
  if (layerWarnings.length > 0) {
6785
7041
  yield* Console7.log(c.yellow(`
6786
7042
  Warnings (${layerWarnings.length}):`));
@@ -6809,26 +7065,26 @@ Total packages for layer ${c.dim(`(${allPackages.length})`)}:`);
6809
7065
  Layer name: ${projectName}-deps`);
6810
7066
  }
6811
7067
  });
6812
- var buildLayer = (projectDir, output, verbose) => Effect42.gen(function* () {
6813
- const outputDir = path10.isAbsolute(output) ? output : path10.resolve(projectDir, output);
6814
- const layerDir = path10.join(outputDir, "nodejs", "node_modules");
6815
- const layerRoot = path10.join(outputDir, "nodejs");
6816
- if (fs5.existsSync(layerRoot)) {
6817
- fs5.rmSync(layerRoot, { recursive: true });
6818
- }
6819
- fs5.mkdirSync(layerDir, { recursive: true });
7068
+ var buildLayer = (projectDir, output, verbose) => Effect43.gen(function* () {
7069
+ const outputDir = path11.isAbsolute(output) ? output : path11.resolve(projectDir, output);
7070
+ const layerDir = path11.join(outputDir, "nodejs", "node_modules");
7071
+ const layerRoot = path11.join(outputDir, "nodejs");
7072
+ if (fs7.existsSync(layerRoot)) {
7073
+ fs7.rmSync(layerRoot, { recursive: true });
7074
+ }
7075
+ fs7.mkdirSync(layerDir, { recursive: true });
6820
7076
  yield* Console7.log(`
6821
7077
  ${c.bold("=== Building Layer Locally ===")}
6822
7078
  `);
6823
7079
  const depWarnings = yield* checkDependencyWarnings(projectDir).pipe(
6824
- Effect42.catchAll(() => Effect42.succeed([]))
7080
+ Effect43.catchAll(() => Effect43.succeed([]))
6825
7081
  );
6826
7082
  for (const w of depWarnings) {
6827
7083
  yield* Console7.log(c.yellow(` \u26A0 ${w}`));
6828
7084
  }
6829
7085
  if (depWarnings.length > 0) yield* Console7.log("");
6830
7086
  const prodDeps = yield* readProductionDependencies(projectDir).pipe(
6831
- Effect42.catchAll(() => Effect42.succeed([]))
7087
+ Effect43.catchAll(() => Effect43.succeed([]))
6832
7088
  );
6833
7089
  if (prodDeps.length === 0) {
6834
7090
  yield* Console7.log("No production dependencies found in package.json");
@@ -6840,11 +7096,11 @@ ${c.bold("=== Building Layer Locally ===")}
6840
7096
  yield* Console7.log(` ${dep}`);
6841
7097
  }
6842
7098
  const hash = yield* computeLockfileHash(projectDir).pipe(
6843
- Effect42.catchAll(() => Effect42.succeed("unknown"))
7099
+ Effect43.catchAll(() => Effect43.succeed("unknown"))
6844
7100
  );
6845
7101
  yield* Console7.log(`
6846
7102
  Lockfile hash: ${hash}`);
6847
- const { packages: allPackages, resolvedPaths, warnings: layerWarnings } = yield* Effect42.sync(() => collectLayerPackages(projectDir, prodDeps));
7103
+ const { packages: allPackages, resolvedPaths, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps));
6848
7104
  if (layerWarnings.length > 0) {
6849
7105
  yield* Console7.log(`
6850
7106
  Warnings (${layerWarnings.length}):`);
@@ -6871,14 +7127,14 @@ Collected ${allPackages.length} packages for layer`);
6871
7127
  }
6872
7128
  continue;
6873
7129
  }
6874
- const destPath = path10.join(layerDir, pkgName);
7130
+ const destPath = path11.join(layerDir, pkgName);
6875
7131
  if (pkgName.startsWith("@")) {
6876
- const scopeDir = path10.join(layerDir, pkgName.split("/")[0] ?? pkgName);
6877
- if (!fs5.existsSync(scopeDir)) {
6878
- fs5.mkdirSync(scopeDir, { recursive: true });
7132
+ const scopeDir = path11.join(layerDir, pkgName.split("/")[0] ?? pkgName);
7133
+ if (!fs7.existsSync(scopeDir)) {
7134
+ fs7.mkdirSync(scopeDir, { recursive: true });
6879
7135
  }
6880
7136
  }
6881
- fs5.cpSync(srcPath, destPath, { recursive: true });
7137
+ fs7.cpSync(srcPath, destPath, { recursive: true });
6882
7138
  copied++;
6883
7139
  }
6884
7140
  yield* Console7.log(c.green(`
@@ -6894,18 +7150,18 @@ To inspect: ls ${layerDir}`);
6894
7150
  // src/cli/commands/config.ts
6895
7151
  import { Args as Args3, Command as Command6 } from "@effect/cli";
6896
7152
  import { Prompt } from "@effect/cli";
6897
- import { Effect as Effect43, Console as Console8, Logger as Logger5, LogLevel as LogLevel5, Option as Option5 } from "effect";
6898
- var loadRequiredParams = (projectOpt, stage, region) => Effect43.gen(function* () {
7153
+ import { Effect as Effect44, Console as Console8, Logger as Logger5, LogLevel as LogLevel5, Option as Option5 } from "effect";
7154
+ var loadRequiredParams = (projectOpt, stage, region) => Effect44.gen(function* () {
6899
7155
  const { config, projectDir } = yield* ProjectConfig;
6900
7156
  const project = Option5.getOrElse(projectOpt, () => config?.name ?? "");
6901
7157
  if (!project) {
6902
7158
  yield* Console8.error("Error: --project is required (or set 'name' in effortless.config.ts)");
6903
- return yield* Effect43.fail(new Error("Missing project name"));
7159
+ return yield* Effect44.fail(new Error("Missing project name"));
6904
7160
  }
6905
7161
  const patterns = getPatternsFromConfig(config);
6906
7162
  if (!patterns) {
6907
7163
  yield* Console8.error("Error: No 'handlers' patterns in config");
6908
- return yield* Effect43.fail(new Error("Missing handler patterns"));
7164
+ return yield* Effect44.fail(new Error("Missing handler patterns"));
6909
7165
  }
6910
7166
  const files = findHandlerFiles(patterns, projectDir);
6911
7167
  const handlers = discoverHandlers(files);
@@ -6917,7 +7173,7 @@ var loadRequiredParams = (projectOpt, stage, region) => Effect43.gen(function* (
6917
7173
  var listCommand = Command6.make(
6918
7174
  "list",
6919
7175
  { project: projectOption, stage: stageOption, region: regionOption, verbose: verboseOption },
6920
- ({ project: projectOpt, stage, region, verbose }) => Effect43.gen(function* () {
7176
+ ({ project: projectOpt, stage, region, verbose }) => Effect44.gen(function* () {
6921
7177
  const ctx = yield* loadRequiredParams(projectOpt, stage, region);
6922
7178
  const { params } = ctx;
6923
7179
  if (params.length === 0) {
@@ -6925,7 +7181,7 @@ var listCommand = Command6.make(
6925
7181
  return;
6926
7182
  }
6927
7183
  const { existing, missing } = yield* checkMissingParams(params).pipe(
6928
- Effect43.provide(clients_exports.makeClients({ ssm: { region: ctx.region } }))
7184
+ Effect44.provide(clients_exports.makeClients({ ssm: { region: ctx.region } }))
6929
7185
  );
6930
7186
  yield* Console8.log(`
6931
7187
  ${c.bold("Config parameters")} ${c.dim(`(${ctx.project} / ${ctx.stage})`)}
@@ -6949,7 +7205,7 @@ ${c.bold("Config parameters")} ${c.dim(`(${ctx.project} / ${ctx.stage})`)}
6949
7205
  }
6950
7206
  yield* Console8.log("");
6951
7207
  }).pipe(
6952
- Effect43.provide(ProjectConfig.Live),
7208
+ Effect44.provide(ProjectConfig.Live),
6953
7209
  Logger5.withMinimumLogLevel(LogLevel5.Warning)
6954
7210
  )
6955
7211
  ).pipe(Command6.withDescription("List all declared config parameters and show which are set vs missing"));
@@ -6959,7 +7215,7 @@ var setKeyArg = Args3.text({ name: "key" }).pipe(
6959
7215
  var setCommand = Command6.make(
6960
7216
  "set",
6961
7217
  { key: setKeyArg, project: projectOption, stage: stageOption, region: regionOption, verbose: verboseOption },
6962
- ({ key, project: projectOpt, stage, region, verbose }) => Effect43.gen(function* () {
7218
+ ({ key, project: projectOpt, stage, region, verbose }) => Effect44.gen(function* () {
6963
7219
  const { config } = yield* ProjectConfig;
6964
7220
  const project = Option5.getOrElse(projectOpt, () => config?.name ?? "");
6965
7221
  if (!project) {
@@ -6977,18 +7233,18 @@ var setCommand = Command6.make(
6977
7233
  Value: value,
6978
7234
  Type: "SecureString",
6979
7235
  Overwrite: true
6980
- }).pipe(Effect43.provide(clients_exports.makeClients({ ssm: { region: finalRegion } })));
7236
+ }).pipe(Effect44.provide(clients_exports.makeClients({ ssm: { region: finalRegion } })));
6981
7237
  yield* Console8.log(`
6982
7238
  ${c.green("\u2713")} ${c.cyan(ssmPath)} ${c.dim("(SecureString)")}`);
6983
7239
  }).pipe(
6984
- Effect43.provide(ProjectConfig.Live),
7240
+ Effect44.provide(ProjectConfig.Live),
6985
7241
  Logger5.withMinimumLogLevel(LogLevel5.Warning)
6986
7242
  )
6987
7243
  ).pipe(Command6.withDescription("Set a config parameter value (stored encrypted in AWS)"));
6988
7244
  var configRootCommand = Command6.make(
6989
7245
  "config",
6990
7246
  { project: projectOption, stage: stageOption, region: regionOption, verbose: verboseOption },
6991
- ({ project: projectOpt, stage, region, verbose }) => Effect43.gen(function* () {
7247
+ ({ project: projectOpt, stage, region, verbose }) => Effect44.gen(function* () {
6992
7248
  const ctx = yield* loadRequiredParams(projectOpt, stage, region);
6993
7249
  const { params } = ctx;
6994
7250
  if (params.length === 0) {
@@ -6996,7 +7252,7 @@ var configRootCommand = Command6.make(
6996
7252
  return;
6997
7253
  }
6998
7254
  const { missing } = yield* checkMissingParams(params).pipe(
6999
- Effect43.provide(clients_exports.makeClients({ ssm: { region: ctx.region } }))
7255
+ Effect44.provide(clients_exports.makeClients({ ssm: { region: ctx.region } }))
7000
7256
  );
7001
7257
  if (missing.length === 0) {
7002
7258
  yield* Console8.log(`
@@ -7021,7 +7277,7 @@ ${c.bold("Missing parameters")} ${c.dim(`(${ctx.project} / ${ctx.stage})`)}
7021
7277
  Value: value,
7022
7278
  Type: "SecureString",
7023
7279
  Overwrite: false
7024
- }).pipe(Effect43.provide(clients_exports.makeClients({ ssm: { region: ctx.region } })));
7280
+ }).pipe(Effect44.provide(clients_exports.makeClients({ ssm: { region: ctx.region } })));
7025
7281
  yield* Console8.log(` ${c.green("\u2713")} created`);
7026
7282
  created++;
7027
7283
  }
@@ -7035,7 +7291,7 @@ ${c.bold("Missing parameters")} ${c.dim(`(${ctx.project} / ${ctx.stage})`)}
7035
7291
  `);
7036
7292
  }
7037
7293
  }).pipe(
7038
- Effect43.provide(ProjectConfig.Live),
7294
+ Effect44.provide(ProjectConfig.Live),
7039
7295
  Logger5.withMinimumLogLevel(LogLevel5.Warning)
7040
7296
  )
7041
7297
  ).pipe(
@@ -7056,7 +7312,7 @@ var cli = Command7.run(mainCommand, {
7056
7312
  version
7057
7313
  });
7058
7314
  cli(process.argv).pipe(
7059
- Effect44.provide(NodeContext.layer),
7060
- Effect44.provide(CliConfig.layer({ showBuiltIns: false, showTypes: false })),
7315
+ Effect45.provide(NodeContext.layer),
7316
+ Effect45.provide(CliConfig.layer({ showBuiltIns: false, showTypes: false })),
7061
7317
  NodeRuntime.runMain
7062
7318
  );