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