@effortless-aws/cli 0.8.0 → 0.9.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 +814 -254
  2. package/package.json +2 -2
package/dist/cli/index.js CHANGED
@@ -6,9 +6,9 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/cli/index.ts
9
- import { CliConfig, Command as Command7 } from "@effect/cli";
9
+ import { CliConfig, Command as Command8 } from "@effect/cli";
10
10
  import { NodeContext, NodeRuntime } from "@effect/platform-node";
11
- import { Effect as Effect45 } from "effect";
11
+ import { Effect as Effect46 } from "effect";
12
12
  import { createRequire as createRequire2 } from "module";
13
13
 
14
14
  // src/cli/commands/deploy.ts
@@ -1933,6 +1933,18 @@ var publishVersion = (functionName) => Effect13.gen(function* () {
1933
1933
  version: result.Version
1934
1934
  };
1935
1935
  });
1936
+ var ensureEdgePermission = (functionName) => lambda_exports.make("add_permission", {
1937
+ FunctionName: functionName,
1938
+ StatementId: "replicator",
1939
+ Action: "lambda:GetFunction",
1940
+ Principal: "replicator.lambda.amazonaws.com"
1941
+ }).pipe(
1942
+ Effect13.catchIf(
1943
+ (e) => e._tag === "LambdaError" && e.is("ResourceConflictException"),
1944
+ () => Effect13.logDebug(`Replicator permission already exists for ${functionName}`)
1945
+ ),
1946
+ Effect13.asVoid
1947
+ );
1936
1948
  var DEFAULT_CORS = {
1937
1949
  AllowOrigins: ["*"],
1938
1950
  AllowMethods: ["*"],
@@ -2459,15 +2471,24 @@ var getPackageVersion = (pkgPath) => {
2459
2471
  return null;
2460
2472
  }
2461
2473
  };
2462
- var computeLockfileHash = (projectDir) => Effect18.gen(function* () {
2474
+ var computeLockfileHash = (projectDir, extraNodeModules) => Effect18.gen(function* () {
2463
2475
  const prodDeps = yield* readProductionDependencies(projectDir);
2464
2476
  if (prodDeps.length === 0) {
2465
2477
  return yield* Effect18.fail(new Error("No production dependencies"));
2466
2478
  }
2467
- const { packages: allPackages, resolvedPaths } = collectTransitiveDeps(projectDir, prodDeps);
2479
+ const { packages: allPackages, resolvedPaths } = collectTransitiveDeps(
2480
+ projectDir,
2481
+ prodDeps,
2482
+ void 0,
2483
+ void 0,
2484
+ void 0,
2485
+ void 0,
2486
+ void 0,
2487
+ extraNodeModules
2488
+ );
2468
2489
  const packageVersions = [];
2469
2490
  for (const pkgName of Array.from(allPackages).filter((p) => !isAwsRuntime(p)).sort()) {
2470
- const pkgPath = resolvedPaths.get(pkgName) ?? findInPnpmStore(projectDir, pkgName) ?? getPackageRealPath(projectDir, pkgName);
2491
+ const pkgPath = resolvedPaths.get(pkgName) ?? findInPnpmStore(projectDir, pkgName, extraNodeModules) ?? getPackageRealPath(projectDir, pkgName);
2471
2492
  if (pkgPath) {
2472
2493
  const version2 = getPackageVersion(pkgPath);
2473
2494
  if (version2) {
@@ -2522,10 +2543,13 @@ var DEV_ONLY_PREFIXES = [
2522
2543
  "@vitest/",
2523
2544
  "@jest/"
2524
2545
  ];
2525
- var extractExportPaths = (value) => {
2546
+ var RUNTIME_CONDITIONS = /* @__PURE__ */ new Set(["import", "require", "default", "node"]);
2547
+ var isSubpath = (key) => key.startsWith(".");
2548
+ var extractExportPaths = (value, key) => {
2549
+ if (key !== void 0 && !isSubpath(key) && !RUNTIME_CONDITIONS.has(key)) return [];
2526
2550
  if (typeof value === "string") return [value];
2527
2551
  if (typeof value === "object" && value !== null) {
2528
- return Object.values(value).flatMap(extractExportPaths);
2552
+ return Object.entries(value).flatMap(([k, v]) => extractExportPaths(v, k));
2529
2553
  }
2530
2554
  return [];
2531
2555
  };
@@ -2568,7 +2592,7 @@ var checkDependencyWarnings = (projectDir) => Effect18.gen(function* () {
2568
2592
  const tsEntries = [...new Set(entryPoints.filter(isRawTypeScript))];
2569
2593
  if (tsEntries.length > 0) {
2570
2594
  warnings.push(
2571
- `Package "${dep}" has TypeScript entry points (${tsEntries.join(", ")}) that will fail at runtime in node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING). Move it to "devDependencies" so esbuild can bundle and transpile it.`
2595
+ `Package "${dep}" has TypeScript entry points (${tsEntries.join(", ")}) \u2014 it will be bundled by esbuild instead of included in the layer.`
2572
2596
  );
2573
2597
  }
2574
2598
  } catch {
@@ -2601,8 +2625,7 @@ var getPackageDeps = (pkgPath) => {
2601
2625
  return EMPTY_DEPS;
2602
2626
  }
2603
2627
  };
2604
- var findInPnpmStore = (projectDir, pkgName) => {
2605
- const pnpmDir = path.join(projectDir, "node_modules", ".pnpm");
2628
+ var searchPnpmDir = (pnpmDir, pkgName) => {
2606
2629
  if (!fsSync.existsSync(pnpmDir)) return null;
2607
2630
  const pnpmPkgName = pkgName.replace("/", "+");
2608
2631
  try {
@@ -2622,7 +2645,18 @@ var findInPnpmStore = (projectDir, pkgName) => {
2622
2645
  }
2623
2646
  return null;
2624
2647
  };
2625
- var collectTransitiveDeps = (projectDir, rootDeps, searchPath = path.join(projectDir, "node_modules"), visited = /* @__PURE__ */ new Set(), resolvedPaths = /* @__PURE__ */ new Map(), warnings = [], optionalNames = /* @__PURE__ */ new Set()) => {
2648
+ var findInPnpmStore = (projectDir, pkgName, extraNodeModules) => {
2649
+ const result = searchPnpmDir(path.join(projectDir, "node_modules", ".pnpm"), pkgName);
2650
+ if (result) return result;
2651
+ if (extraNodeModules) {
2652
+ for (const dir of extraNodeModules) {
2653
+ const found = searchPnpmDir(path.join(dir, ".pnpm"), pkgName);
2654
+ if (found) return found;
2655
+ }
2656
+ }
2657
+ return null;
2658
+ };
2659
+ var collectTransitiveDeps = (projectDir, rootDeps, searchPath = path.join(projectDir, "node_modules"), visited = /* @__PURE__ */ new Set(), resolvedPaths = /* @__PURE__ */ new Map(), warnings = [], optionalNames = /* @__PURE__ */ new Set(), extraNodeModules) => {
2626
2660
  const rootNodeModules = path.join(projectDir, "node_modules");
2627
2661
  for (const dep of rootDeps) {
2628
2662
  if (visited.has(dep)) continue;
@@ -2645,8 +2679,20 @@ var collectTransitiveDeps = (projectDir, rootDeps, searchPath = path.join(projec
2645
2679
  }
2646
2680
  }
2647
2681
  }
2682
+ if (!realPath && extraNodeModules) {
2683
+ for (const dir of extraNodeModules) {
2684
+ pkgPath = path.join(dir, dep);
2685
+ if (fsSync.existsSync(pkgPath)) {
2686
+ try {
2687
+ realPath = fsSync.realpathSync(pkgPath);
2688
+ break;
2689
+ } catch {
2690
+ }
2691
+ }
2692
+ }
2693
+ }
2648
2694
  if (!realPath) {
2649
- realPath = findInPnpmStore(projectDir, dep);
2695
+ realPath = findInPnpmStore(projectDir, dep, extraNodeModules);
2650
2696
  }
2651
2697
  if (!realPath) {
2652
2698
  if (!optionalNames.has(dep)) {
@@ -2662,15 +2708,38 @@ var collectTransitiveDeps = (projectDir, rootDeps, searchPath = path.join(projec
2662
2708
  const pkgNodeModules = isScoped ? path.dirname(path.dirname(realPath)) : path.dirname(realPath);
2663
2709
  const nextOptional = new Set(optionalNames);
2664
2710
  for (const name of pkgDeps.optional) nextOptional.add(name);
2665
- collectTransitiveDeps(projectDir, pkgDeps.all, pkgNodeModules, visited, resolvedPaths, warnings, nextOptional);
2711
+ collectTransitiveDeps(projectDir, pkgDeps.all, pkgNodeModules, visited, resolvedPaths, warnings, nextOptional, extraNodeModules);
2666
2712
  }
2667
2713
  }
2668
2714
  return { packages: visited, resolvedPaths, warnings };
2669
2715
  };
2670
2716
  var isAwsRuntime = (pkg) => pkg.startsWith("@aws-sdk/") || pkg.startsWith("@smithy/") || pkg.startsWith("@aws-crypto/") || pkg.startsWith("@aws/");
2671
- var collectLayerPackages = (projectDir, dependencies) => {
2717
+ var hasTypeScriptEntryPoints = (pkgPath) => {
2718
+ const pkgJsonPath = path.join(pkgPath, "package.json");
2719
+ if (!fsSync.existsSync(pkgJsonPath)) return false;
2720
+ try {
2721
+ const pkgJson = JSON.parse(fsSync.readFileSync(pkgJsonPath, "utf-8"));
2722
+ const entryPoints = [];
2723
+ if (typeof pkgJson.main === "string") entryPoints.push(pkgJson.main);
2724
+ if (typeof pkgJson.module === "string") entryPoints.push(pkgJson.module);
2725
+ entryPoints.push(...extractExportPaths(pkgJson.exports));
2726
+ return entryPoints.some(isRawTypeScript);
2727
+ } catch {
2728
+ return false;
2729
+ }
2730
+ };
2731
+ var collectLayerPackages = (projectDir, dependencies, extraNodeModules) => {
2672
2732
  if (dependencies.length === 0) return { packages: [], resolvedPaths: /* @__PURE__ */ new Map(), warnings: [] };
2673
- const { packages, resolvedPaths, warnings } = collectTransitiveDeps(projectDir, dependencies);
2733
+ const { packages, resolvedPaths, warnings } = collectTransitiveDeps(
2734
+ projectDir,
2735
+ dependencies,
2736
+ void 0,
2737
+ void 0,
2738
+ void 0,
2739
+ void 0,
2740
+ void 0,
2741
+ extraNodeModules
2742
+ );
2674
2743
  let changed = true;
2675
2744
  while (changed) {
2676
2745
  changed = false;
@@ -2679,7 +2748,7 @@ var collectLayerPackages = (projectDir, dependencies) => {
2679
2748
  const pkgPaths = /* @__PURE__ */ new Set();
2680
2749
  const resolved = resolvedPaths.get(pkg);
2681
2750
  if (resolved) pkgPaths.add(resolved);
2682
- const found = findPackagePath(projectDir, pkg);
2751
+ const found = findPackagePath(projectDir, pkg, extraNodeModules);
2683
2752
  if (found) pkgPaths.add(found);
2684
2753
  if (pkgPaths.size === 0) continue;
2685
2754
  for (const pkgPath of pkgPaths) {
@@ -2687,7 +2756,7 @@ var collectLayerPackages = (projectDir, dependencies) => {
2687
2756
  const optionalSet = new Set(pkgDeps.optional);
2688
2757
  for (const dep of pkgDeps.all) {
2689
2758
  if (!packages.has(dep) && !isAwsRuntime(dep)) {
2690
- let depPath = findPackagePath(projectDir, dep);
2759
+ let depPath = findPackagePath(projectDir, dep, extraNodeModules);
2691
2760
  if (!depPath) {
2692
2761
  const isScoped = pkg.startsWith("@");
2693
2762
  const parentNodeModules = isScoped ? path.dirname(path.dirname(pkgPath)) : path.dirname(pkgPath);
@@ -2708,13 +2777,33 @@ var collectLayerPackages = (projectDir, dependencies) => {
2708
2777
  }
2709
2778
  }
2710
2779
  }
2711
- const filtered = Array.from(packages).filter((pkg) => !isAwsRuntime(pkg));
2780
+ const filtered = Array.from(packages).filter((pkg) => {
2781
+ if (isAwsRuntime(pkg)) return false;
2782
+ const pkgPath = resolvedPaths.get(pkg) ?? findPackagePath(projectDir, pkg, extraNodeModules);
2783
+ if (!pkgPath) return false;
2784
+ if (hasTypeScriptEntryPoints(pkgPath)) {
2785
+ warnings.push(`Package "${pkg}" has TypeScript entry points \u2014 it will be bundled by esbuild instead of included in the layer`);
2786
+ return false;
2787
+ }
2788
+ return true;
2789
+ });
2712
2790
  return { packages: filtered, resolvedPaths, warnings };
2713
2791
  };
2714
- var findPackagePath = (projectDir, pkgName) => {
2792
+ var findPackagePath = (projectDir, pkgName, extraNodeModules) => {
2715
2793
  const rootPath = getPackageRealPath(projectDir, pkgName);
2716
2794
  if (rootPath) return rootPath;
2717
- return findInPnpmStore(projectDir, pkgName);
2795
+ if (extraNodeModules) {
2796
+ for (const dir of extraNodeModules) {
2797
+ const pkgPath = path.join(dir, pkgName);
2798
+ if (fsSync.existsSync(pkgPath)) {
2799
+ try {
2800
+ return fsSync.realpathSync(pkgPath);
2801
+ } catch {
2802
+ }
2803
+ }
2804
+ }
2805
+ }
2806
+ return findInPnpmStore(projectDir, pkgName, extraNodeModules);
2718
2807
  };
2719
2808
  var createLayerZip = (projectDir, packages, resolvedPaths) => Effect18.async((resume) => {
2720
2809
  const chunks = [];
@@ -2778,7 +2867,7 @@ var ensureLayer = (config) => Effect18.gen(function* () {
2778
2867
  yield* Effect18.logDebug("No production dependencies, skipping layer creation");
2779
2868
  return null;
2780
2869
  }
2781
- const hash = yield* computeLockfileHash(config.projectDir).pipe(
2870
+ const hash = yield* computeLockfileHash(config.projectDir, config.extraNodeModules).pipe(
2782
2871
  Effect18.catchAll((e) => {
2783
2872
  const message = e instanceof Error ? e.message : String(e);
2784
2873
  return Effect18.logWarning(`Cannot compute lockfile hash: ${message}, skipping layer`).pipe(
@@ -2795,7 +2884,7 @@ var ensureLayer = (config) => Effect18.gen(function* () {
2795
2884
  yield* Effect18.logDebug(`Layer ${layerName} with hash ${hash} already exists (version ${existing.version})`);
2796
2885
  return existing;
2797
2886
  }
2798
- const { packages: allPackages, resolvedPaths, warnings: layerWarnings } = yield* Effect18.sync(() => collectLayerPackages(config.projectDir, dependencies));
2887
+ const { packages: allPackages, resolvedPaths, warnings: layerWarnings } = yield* Effect18.sync(() => collectLayerPackages(config.projectDir, dependencies, config.extraNodeModules));
2799
2888
  for (const warning of layerWarnings) {
2800
2889
  yield* Effect18.logWarning(`[layer] ${warning}`);
2801
2890
  }
@@ -2805,11 +2894,46 @@ var ensureLayer = (config) => Effect18.gen(function* () {
2805
2894
  if (skippedPackages.length > 0) {
2806
2895
  yield* Effect18.logWarning(`Skipped ${skippedPackages.length} packages (not found): ${skippedPackages.slice(0, 10).join(", ")}${skippedPackages.length > 10 ? "..." : ""}`);
2807
2896
  }
2808
- yield* Effect18.logDebug(`Layer zip size: ${(layerZip.length / 1024 / 1024).toFixed(2)} MB (${includedPackages.length} packages)`);
2897
+ const zipSizeMB = layerZip.length / 1024 / 1024;
2898
+ yield* Effect18.logDebug(`Layer zip size: ${zipSizeMB.toFixed(2)} MB (${includedPackages.length} packages)`);
2899
+ const MAX_DIRECT_UPLOAD = 50 * 1024 * 1024;
2900
+ let content;
2901
+ if (layerZip.length > MAX_DIRECT_UPLOAD) {
2902
+ const bucketName = `${config.project}-${config.stage}-deploy-artifacts`;
2903
+ const s3Key = `layers/${layerName}-${hash}.zip`;
2904
+ yield* Effect18.logDebug(`Layer zip too large for direct upload (${zipSizeMB.toFixed(1)} MB), uploading to S3: s3://${bucketName}/${s3Key}`);
2905
+ const bucketExists = yield* s3_exports.make("head_bucket", { Bucket: bucketName }).pipe(
2906
+ Effect18.map(() => true),
2907
+ Effect18.catchIf((e) => e._tag === "S3Error", () => Effect18.succeed(false))
2908
+ );
2909
+ if (!bucketExists) {
2910
+ yield* s3_exports.make("create_bucket", {
2911
+ Bucket: bucketName,
2912
+ ...config.region !== "us-east-1" ? { CreateBucketConfiguration: { LocationConstraint: config.region } } : {}
2913
+ });
2914
+ yield* s3_exports.make("put_public_access_block", {
2915
+ Bucket: bucketName,
2916
+ PublicAccessBlockConfiguration: {
2917
+ BlockPublicAcls: true,
2918
+ IgnorePublicAcls: true,
2919
+ BlockPublicPolicy: true,
2920
+ RestrictPublicBuckets: true
2921
+ }
2922
+ });
2923
+ }
2924
+ yield* s3_exports.make("put_object", {
2925
+ Bucket: bucketName,
2926
+ Key: s3Key,
2927
+ Body: layerZip
2928
+ });
2929
+ content = { S3Bucket: bucketName, S3Key: s3Key };
2930
+ } else {
2931
+ content = { ZipFile: layerZip };
2932
+ }
2809
2933
  const result = yield* lambda_exports.make("publish_layer_version", {
2810
2934
  LayerName: layerName,
2811
2935
  Description: `effortless deps layer hash:${hash}`,
2812
- Content: { ZipFile: layerZip },
2936
+ Content: content,
2813
2937
  CompatibleRuntimes: [Runtime2.nodejs24x],
2814
2938
  CompatibleArchitectures: [Architecture2.arm64]
2815
2939
  });
@@ -4002,6 +4126,7 @@ import { Effect as Effect24 } from "effect";
4002
4126
  import * as esbuild from "esbuild";
4003
4127
  import * as fsSync2 from "fs";
4004
4128
  import * as path3 from "path";
4129
+ import { builtinModules } from "module";
4005
4130
  import { createRequire } from "module";
4006
4131
  import archiver2 from "archiver";
4007
4132
  import { globSync } from "glob";
@@ -4012,7 +4137,36 @@ var parseSource = (source) => {
4012
4137
  const project = new Project({ useInMemoryFileSystem: true });
4013
4138
  return project.createSourceFile("input.ts", source);
4014
4139
  };
4015
- var RUNTIME_PROPS = ["onRecord", "onBatchComplete", "onBatch", "onMessage", "onObjectCreated", "onObjectRemoved", "setup", "schema", "onError", "deps", "config", "static", "middleware", "routes", "get", "post"];
4140
+ var bareName = (expr) => {
4141
+ const dot = expr.lastIndexOf(".");
4142
+ return dot === -1 ? expr : expr.slice(dot + 1);
4143
+ };
4144
+ var getProp = (obj, name) => obj.getProperties().find((p) => p.getKind() === SyntaxKind.PropertyAssignment && p.getName() === name)?.asKindOrThrow(SyntaxKind.PropertyAssignment).getInitializer();
4145
+ var findDefineCalls = (sourceFile, defineFn) => {
4146
+ const results = [];
4147
+ const tryAdd = (callExpr, exportName) => {
4148
+ if (bareName(callExpr.getExpression().getText()) !== defineFn) return;
4149
+ const firstArg = callExpr.getArguments()[0];
4150
+ if (firstArg?.getKind() === SyntaxKind.ObjectLiteralExpression) {
4151
+ results.push({ exportName, args: firstArg });
4152
+ }
4153
+ };
4154
+ const def = sourceFile.getExportAssignment((e) => !e.isExportEquals());
4155
+ if (def?.getExpression().getKind() === SyntaxKind.CallExpression) {
4156
+ tryAdd(def.getExpression().asKindOrThrow(SyntaxKind.CallExpression), "default");
4157
+ }
4158
+ for (const stmt of sourceFile.getVariableStatements()) {
4159
+ if (!stmt.isExported()) continue;
4160
+ for (const decl of stmt.getDeclarations()) {
4161
+ const init = decl.getInitializer();
4162
+ if (init?.getKind() === SyntaxKind.CallExpression) {
4163
+ tryAdd(init.asKindOrThrow(SyntaxKind.CallExpression), decl.getName());
4164
+ }
4165
+ }
4166
+ }
4167
+ return results;
4168
+ };
4169
+ var RUNTIME_PROPS = ["onRecord", "onBatchComplete", "onBatch", "onMessage", "onObjectCreated", "onObjectRemoved", "setup", "schema", "onError", "deps", "config", "static", "middleware", "auth", "routes", "get", "post"];
4016
4170
  var evalConfig = (configText, exportName) => {
4017
4171
  try {
4018
4172
  return new Function(`return ${configText}`)();
@@ -4027,35 +4181,18 @@ Handler config must use only literal values (no variables, imports, or expressio
4027
4181
  var buildConfigWithoutRuntime = (obj) => {
4028
4182
  const props = obj.getProperties().filter((p) => {
4029
4183
  if (p.getKind() === SyntaxKind.PropertyAssignment) {
4030
- const propAssign = p;
4031
- return !RUNTIME_PROPS.includes(propAssign.getName());
4184
+ return !RUNTIME_PROPS.includes(p.getName());
4185
+ }
4186
+ if (p.getKind() === SyntaxKind.ShorthandPropertyAssignment) {
4187
+ return !RUNTIME_PROPS.includes(p.asKindOrThrow(SyntaxKind.ShorthandPropertyAssignment).getName());
4032
4188
  }
4033
4189
  return true;
4034
4190
  }).map((p) => p.getText()).join(", ");
4035
4191
  return `{ ${props} }`;
4036
4192
  };
4037
- var extractPropertyFromObject = (obj, propName) => {
4038
- const prop = obj.getProperties().find((p) => {
4039
- if (p.getKind() === SyntaxKind.PropertyAssignment) {
4040
- return p.getName() === propName;
4041
- }
4042
- return false;
4043
- });
4044
- if (prop && prop.getKind() === SyntaxKind.PropertyAssignment) {
4045
- const propAssign = prop;
4046
- return propAssign.getInitializer()?.getText();
4047
- }
4048
- return void 0;
4049
- };
4193
+ var toKebabCase = (str) => str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
4050
4194
  var extractDepsKeys = (obj) => {
4051
- const depsProp = obj.getProperties().find((p) => {
4052
- if (p.getKind() === SyntaxKind.PropertyAssignment) {
4053
- return p.getName() === "deps";
4054
- }
4055
- return false;
4056
- });
4057
- if (!depsProp || depsProp.getKind() !== SyntaxKind.PropertyAssignment) return [];
4058
- let init = depsProp.getInitializer();
4195
+ let init = getProp(obj, "deps");
4059
4196
  if (!init) return [];
4060
4197
  if (init.getKind() === SyntaxKind.ArrowFunction) {
4061
4198
  const body = init.getBody();
@@ -4063,9 +4200,8 @@ var extractDepsKeys = (obj) => {
4063
4200
  init = body.getExpression();
4064
4201
  }
4065
4202
  }
4066
- if (!init || init.getKind() !== SyntaxKind.ObjectLiteralExpression) return [];
4067
- const depsObj = init;
4068
- return depsObj.getProperties().map((p) => {
4203
+ if (init.getKind() !== SyntaxKind.ObjectLiteralExpression) return [];
4204
+ return init.getProperties().map((p) => {
4069
4205
  if (p.getKind() === SyntaxKind.ShorthandPropertyAssignment) {
4070
4206
  return p.asKindOrThrow(SyntaxKind.ShorthandPropertyAssignment).getName();
4071
4207
  }
@@ -4075,15 +4211,17 @@ var extractDepsKeys = (obj) => {
4075
4211
  return "";
4076
4212
  }).filter(Boolean);
4077
4213
  };
4078
- var extractParamEntries = (obj) => {
4079
- const configProp = obj.getProperties().find((p) => {
4080
- if (p.getKind() === SyntaxKind.PropertyAssignment) {
4081
- return p.getName() === "config";
4082
- }
4083
- return false;
4084
- });
4085
- if (!configProp || configProp.getKind() !== SyntaxKind.PropertyAssignment) return [];
4086
- const init = configProp.getInitializer();
4214
+ var parseGenerateSpec = (text) => {
4215
+ if (!text) return void 0;
4216
+ const hexMatch = text.match(/generateHex\((\d+)\)/);
4217
+ if (hexMatch) return { type: "hex", bytes: Number(hexMatch[1]) };
4218
+ const base64Match = text.match(/generateBase64\((\d+)\)/);
4219
+ if (base64Match) return { type: "base64", bytes: Number(base64Match[1]) };
4220
+ if (text.includes("generateUuid")) return { type: "uuid" };
4221
+ return void 0;
4222
+ };
4223
+ var extractSecretEntries = (obj) => {
4224
+ const init = getProp(obj, "config");
4087
4225
  if (!init || init.getKind() !== SyntaxKind.ObjectLiteralExpression) return [];
4088
4226
  const configObj = init;
4089
4227
  const entries = [];
@@ -4094,47 +4232,46 @@ var extractParamEntries = (obj) => {
4094
4232
  const propInit = propAssign.getInitializer();
4095
4233
  if (!propInit) continue;
4096
4234
  if (propInit.getKind() === SyntaxKind.StringLiteral) {
4097
- const ssmKey = propInit.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue();
4098
- entries.push({ propName, ssmKey });
4235
+ entries.push({ propName, ssmKey: propInit.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue() });
4099
4236
  continue;
4100
4237
  }
4101
4238
  if (propInit.getKind() !== SyntaxKind.CallExpression) continue;
4102
4239
  const callExpr = propInit;
4103
- const callArgs = callExpr.getArguments();
4104
- if (callArgs.length === 0) continue;
4105
- const firstArg = callArgs[0];
4106
- if (firstArg.getKind() === SyntaxKind.StringLiteral) {
4107
- const ssmKey = firstArg.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue();
4108
- entries.push({ propName, ssmKey });
4240
+ const fnName = bareName(callExpr.getExpression().getText());
4241
+ if (fnName === "secret") {
4242
+ const callArgs = callExpr.getArguments();
4243
+ if (callArgs.length === 0) {
4244
+ entries.push({ propName, ssmKey: toKebabCase(propName) });
4245
+ continue;
4246
+ }
4247
+ const firstArg = callArgs[0];
4248
+ if (firstArg.getKind() === SyntaxKind.ObjectLiteralExpression) {
4249
+ const optObj = firstArg;
4250
+ const keyText = getProp(optObj, "key")?.getText();
4251
+ const ssmKey = keyText ? keyText.replace(/^["']|["']$/g, "") : toKebabCase(propName);
4252
+ const generate = parseGenerateSpec(getProp(optObj, "generate")?.getText());
4253
+ entries.push({ propName, ssmKey, ...generate ? { generate } : {} });
4254
+ }
4255
+ continue;
4256
+ }
4257
+ if (fnName === "param") {
4258
+ const firstArg = callExpr.getArguments()[0];
4259
+ if (firstArg?.getKind() === SyntaxKind.StringLiteral) {
4260
+ entries.push({ propName, ssmKey: firstArg.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue() });
4261
+ }
4109
4262
  }
4110
4263
  }
4111
4264
  return entries;
4112
4265
  };
4113
4266
  var extractStaticGlobs = (obj) => {
4114
- const staticProp = obj.getProperties().find((p) => {
4115
- if (p.getKind() === SyntaxKind.PropertyAssignment) {
4116
- return p.getName() === "static";
4117
- }
4118
- return false;
4119
- });
4120
- if (!staticProp || staticProp.getKind() !== SyntaxKind.PropertyAssignment) return [];
4121
- const init = staticProp.getInitializer();
4267
+ const init = getProp(obj, "static");
4122
4268
  if (!init || init.getKind() !== SyntaxKind.ArrayLiteralExpression) return [];
4123
- const arrayLiteral = init;
4124
- return arrayLiteral.getElements().filter((e) => e.getKind() === SyntaxKind.StringLiteral).map((e) => e.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue());
4269
+ return init.asKindOrThrow(SyntaxKind.ArrayLiteralExpression).getElements().filter((e) => e.getKind() === SyntaxKind.StringLiteral).map((e) => e.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue());
4125
4270
  };
4126
4271
  var extractRoutePatterns = (obj) => {
4127
- const routesProp = obj.getProperties().find((p) => {
4128
- if (p.getKind() === SyntaxKind.PropertyAssignment) {
4129
- return p.getName() === "routes";
4130
- }
4131
- return false;
4132
- });
4133
- if (!routesProp || routesProp.getKind() !== SyntaxKind.PropertyAssignment) return [];
4134
- const init = routesProp.getInitializer();
4272
+ const init = getProp(obj, "routes");
4135
4273
  if (!init || init.getKind() !== SyntaxKind.ObjectLiteralExpression) return [];
4136
- const routesObj = init;
4137
- return routesObj.getProperties().map((p) => {
4274
+ return init.getProperties().map((p) => {
4138
4275
  if (p.getKind() !== SyntaxKind.PropertyAssignment) return "";
4139
4276
  const nameNode = p.getNameNode();
4140
4277
  if (nameNode.getKind() === SyntaxKind.StringLiteral) {
@@ -4143,6 +4280,30 @@ var extractRoutePatterns = (obj) => {
4143
4280
  return nameNode.getText();
4144
4281
  }).filter(Boolean);
4145
4282
  };
4283
+ var extractAuthConfigFromCall = (callExpr) => {
4284
+ if (bareName(callExpr.getExpression().getText()) !== "defineAuth") return void 0;
4285
+ const firstArg = callExpr.getArguments()[0];
4286
+ if (!firstArg || firstArg.getKind() !== SyntaxKind.ObjectLiteralExpression) return void 0;
4287
+ try {
4288
+ return new Function(`return ${firstArg.getText()}`)();
4289
+ } catch {
4290
+ return void 0;
4291
+ }
4292
+ };
4293
+ var extractAuthConfig = (obj, sourceFile) => {
4294
+ const init = getProp(obj, "auth");
4295
+ if (!init) return void 0;
4296
+ if (init.getKind() === SyntaxKind.CallExpression) {
4297
+ return extractAuthConfigFromCall(init);
4298
+ }
4299
+ if (init.getKind() === SyntaxKind.Identifier) {
4300
+ const varInit = sourceFile.getVariableDeclaration(init.getText())?.getInitializer();
4301
+ if (varInit?.getKind() === SyntaxKind.CallExpression) {
4302
+ return extractAuthConfigFromCall(varInit);
4303
+ }
4304
+ }
4305
+ return void 0;
4306
+ };
4146
4307
  var handlerRegistry = {
4147
4308
  table: {
4148
4309
  defineFn: "defineTable",
@@ -4190,96 +4351,49 @@ var handlerRegistry = {
4190
4351
  var extractHandlerConfigs = (source, type) => {
4191
4352
  const { defineFn, handlerProps } = handlerRegistry[type];
4192
4353
  const sourceFile = parseSource(source);
4193
- const results = [];
4194
- const exportDefault = sourceFile.getExportAssignment((e) => !e.isExportEquals());
4195
- if (exportDefault) {
4196
- const expr = exportDefault.getExpression();
4197
- if (expr.getKind() === SyntaxKind.CallExpression) {
4198
- const callExpr = expr.asKindOrThrow(SyntaxKind.CallExpression);
4199
- if (callExpr.getExpression().getText() === defineFn) {
4200
- const args = callExpr.getArguments();
4201
- const firstArg = args[0];
4202
- if (firstArg && firstArg.getKind() === SyntaxKind.ObjectLiteralExpression) {
4203
- const objLiteral = firstArg;
4204
- const configText = buildConfigWithoutRuntime(objLiteral);
4205
- const config = evalConfig(configText, "default");
4206
- const hasHandler = handlerProps.some((p) => extractPropertyFromObject(objLiteral, p) !== void 0);
4207
- const depsKeys = extractDepsKeys(objLiteral);
4208
- const paramEntries = extractParamEntries(objLiteral);
4209
- const staticGlobs = extractStaticGlobs(objLiteral);
4210
- const routePatterns = extractRoutePatterns(objLiteral);
4211
- results.push({ exportName: "default", config, hasHandler, depsKeys, paramEntries, staticGlobs, routePatterns });
4212
- }
4213
- }
4214
- }
4215
- }
4216
- sourceFile.getVariableStatements().forEach((stmt) => {
4217
- if (!stmt.isExported()) return;
4218
- stmt.getDeclarations().forEach((decl) => {
4219
- const initializer = decl.getInitializer();
4220
- if (!initializer || initializer.getKind() !== SyntaxKind.CallExpression) return;
4221
- const callExpr = initializer.asKindOrThrow(SyntaxKind.CallExpression);
4222
- if (callExpr.getExpression().getText() !== defineFn) return;
4223
- const args = callExpr.getArguments();
4224
- const firstArg = args[0];
4225
- if (firstArg && firstArg.getKind() === SyntaxKind.ObjectLiteralExpression) {
4226
- const objLiteral = firstArg;
4227
- const configText = buildConfigWithoutRuntime(objLiteral);
4228
- const exportName = decl.getName();
4229
- const config = evalConfig(configText, exportName);
4230
- const hasHandler = handlerProps.some((p) => extractPropertyFromObject(objLiteral, p) !== void 0);
4231
- const depsKeys = extractDepsKeys(objLiteral);
4232
- const paramEntries = extractParamEntries(objLiteral);
4233
- const staticGlobs = extractStaticGlobs(objLiteral);
4234
- const routePatterns = extractRoutePatterns(objLiteral);
4235
- results.push({ exportName, config, hasHandler, depsKeys, paramEntries, staticGlobs, routePatterns });
4236
- }
4237
- });
4354
+ return findDefineCalls(sourceFile, defineFn).map(({ exportName, args }) => {
4355
+ const config = evalConfig(buildConfigWithoutRuntime(args), exportName);
4356
+ const hasHandler = handlerProps.some((p) => getProp(args, p) !== void 0);
4357
+ const authCfg = extractAuthConfig(args, sourceFile);
4358
+ return {
4359
+ exportName,
4360
+ config,
4361
+ hasHandler,
4362
+ depsKeys: extractDepsKeys(args),
4363
+ secretEntries: extractSecretEntries(args),
4364
+ staticGlobs: extractStaticGlobs(args),
4365
+ routePatterns: extractRoutePatterns(args),
4366
+ ...authCfg ? { authConfig: authCfg } : {}
4367
+ };
4238
4368
  });
4239
- return results;
4240
4369
  };
4241
4370
  var generateMiddlewareEntryPoint = (source, runtimeDir2) => {
4242
4371
  const sourceFile = parseSource(source);
4243
- const imports = sourceFile.getImportDeclarations().map((d) => d.getText()).join("\n");
4244
- const defineFn = handlerRegistry.staticSite.defineFn;
4372
+ const calls = findDefineCalls(sourceFile, handlerRegistry.staticSite.defineFn);
4245
4373
  let middlewareFnText;
4246
4374
  let exportName;
4247
- const exportDefault = sourceFile.getExportAssignment((e) => !e.isExportEquals());
4248
- if (exportDefault) {
4249
- const expr = exportDefault.getExpression();
4250
- if (expr.getKind() === SyntaxKind.CallExpression) {
4251
- const callExpr = expr.asKindOrThrow(SyntaxKind.CallExpression);
4252
- if (callExpr.getExpression().getText() === defineFn) {
4253
- const args = callExpr.getArguments();
4254
- const firstArg = args[0];
4255
- if (firstArg?.getKind() === SyntaxKind.ObjectLiteralExpression) {
4256
- middlewareFnText = extractPropertyFromObject(firstArg, "middleware");
4257
- exportName = "default";
4258
- }
4259
- }
4375
+ for (const call of calls) {
4376
+ const mw = getProp(call.args, "middleware")?.getText();
4377
+ if (mw) {
4378
+ middlewareFnText = mw;
4379
+ exportName = call.exportName;
4380
+ break;
4260
4381
  }
4261
4382
  }
4262
- if (!middlewareFnText) {
4263
- sourceFile.getVariableStatements().forEach((stmt) => {
4264
- if (middlewareFnText || !stmt.isExported()) return;
4265
- stmt.getDeclarations().forEach((decl) => {
4266
- if (middlewareFnText) return;
4267
- const init = decl.getInitializer();
4268
- if (!init || init.getKind() !== SyntaxKind.CallExpression) return;
4269
- const callExpr = init.asKindOrThrow(SyntaxKind.CallExpression);
4270
- if (callExpr.getExpression().getText() !== defineFn) return;
4271
- const args = callExpr.getArguments();
4272
- const firstArg = args[0];
4273
- if (firstArg?.getKind() === SyntaxKind.ObjectLiteralExpression) {
4274
- middlewareFnText = extractPropertyFromObject(firstArg, "middleware");
4275
- exportName = decl.getName();
4276
- }
4277
- });
4278
- });
4279
- }
4280
4383
  if (!middlewareFnText || !exportName) {
4281
4384
  throw new Error("Could not extract middleware function from source");
4282
4385
  }
4386
+ const imports = sourceFile.getImportDeclarations().filter((d) => {
4387
+ const defaultImport = d.getDefaultImport()?.getText();
4388
+ if (defaultImport && middlewareFnText.includes(defaultImport)) return true;
4389
+ for (const spec of d.getNamedImports()) {
4390
+ const alias = spec.getAliasNode()?.getText() ?? spec.getName();
4391
+ if (middlewareFnText.includes(alias)) return true;
4392
+ }
4393
+ const ns = d.getNamespaceImport()?.getText();
4394
+ if (ns && middlewareFnText.includes(ns)) return true;
4395
+ return false;
4396
+ }).map((d) => d.getText()).join("\n");
4283
4397
  const wrapperPath = runtimeDir2 ? handlerRegistry.staticSite.wrapperPath.replace("~/runtime", runtimeDir2) : handlerRegistry.staticSite.wrapperPath;
4284
4398
  const entryPoint = `${imports}
4285
4399
  import { wrapMiddlewareFn } from "${wrapperPath}";
@@ -4300,6 +4414,7 @@ export const handler = ${wrapperFn}(${importName});
4300
4414
  };
4301
4415
 
4302
4416
  // src/build/bundle.ts
4417
+ var AUTH_COOKIE_NAME = "__eff_session";
4303
4418
  var extractTableConfigs = (source) => extractHandlerConfigs(source, "table");
4304
4419
  var extractAppConfigs = (source) => extractHandlerConfigs(source, "app");
4305
4420
  var extractStaticSiteConfigs = (source) => extractHandlerConfigs(source, "staticSite");
@@ -4316,7 +4431,8 @@ var bundle = (input) => Effect24.gen(function* () {
4316
4431
  const sourcePath = path3.isAbsolute(input.file) ? input.file : `./${input.file}`;
4317
4432
  const entryPoint = generateEntryPoint(sourcePath, exportName, type, runtimeDir);
4318
4433
  const awsExternals = ["@aws-sdk/*", "@smithy/*"];
4319
- const allExternals = [.../* @__PURE__ */ new Set([...awsExternals, ...externals])];
4434
+ const nodeExternals = builtinModules.flatMap((m) => [m, `node:${m}`]);
4435
+ const allExternals = [.../* @__PURE__ */ new Set([...awsExternals, ...nodeExternals, ...externals])];
4320
4436
  const result = yield* Effect24.tryPromise({
4321
4437
  try: () => esbuild.build({
4322
4438
  stdin: {
@@ -4331,7 +4447,8 @@ var bundle = (input) => Effect24.gen(function* () {
4331
4447
  minify: false,
4332
4448
  sourcemap: false,
4333
4449
  format: input.format ?? "esm",
4334
- external: allExternals
4450
+ external: allExternals,
4451
+ metafile: true
4335
4452
  }),
4336
4453
  catch: (error) => new Error(`esbuild failed: ${error}`)
4337
4454
  });
@@ -4339,8 +4456,32 @@ var bundle = (input) => Effect24.gen(function* () {
4339
4456
  if (!output) {
4340
4457
  throw new Error("esbuild produced no output");
4341
4458
  }
4342
- return output.text;
4459
+ const bundleResult = { code: output.text };
4460
+ if (result.metafile) {
4461
+ bundleResult.topModules = analyzeMetafile(result.metafile);
4462
+ }
4463
+ return bundleResult;
4343
4464
  });
4465
+ var analyzeMetafile = (metafile) => {
4466
+ const packageSizes = /* @__PURE__ */ new Map();
4467
+ for (const [filePath, info] of Object.entries(metafile.inputs)) {
4468
+ const nodeModIdx = filePath.lastIndexOf("node_modules/");
4469
+ let key;
4470
+ if (nodeModIdx !== -1) {
4471
+ const afterNm = filePath.slice(nodeModIdx + "node_modules/".length);
4472
+ if (afterNm.startsWith("@")) {
4473
+ const parts = afterNm.split("/");
4474
+ key = `${parts[0]}/${parts[1]}`;
4475
+ } else {
4476
+ key = afterNm.split("/")[0];
4477
+ }
4478
+ } else {
4479
+ key = "<project>";
4480
+ }
4481
+ packageSizes.set(key, (packageSizes.get(key) ?? 0) + info.bytes);
4482
+ }
4483
+ return Array.from(packageSizes.entries()).map(([p, bytes]) => ({ path: p, bytes })).sort((a, b) => b.bytes - a.bytes);
4484
+ };
4344
4485
  var bundleMiddleware = (input) => Effect24.gen(function* () {
4345
4486
  const absFile = path3.isAbsolute(input.file) ? input.file : path3.resolve(input.projectDir, input.file);
4346
4487
  const source = fsSync2.readFileSync(absFile, "utf-8");
@@ -4371,6 +4512,106 @@ var bundleMiddleware = (input) => Effect24.gen(function* () {
4371
4512
  }
4372
4513
  return output.text;
4373
4514
  });
4515
+ var bundleAuthMiddleware = (input) => Effect24.gen(function* () {
4516
+ const { authConfig, secret } = input;
4517
+ const loginPath = authConfig.loginPath;
4518
+ const publicPatterns = authConfig.public ?? [];
4519
+ const entryPoint = `
4520
+ import { createHmac } from "crypto";
4521
+
4522
+ const SECRET = ${JSON.stringify(secret)};
4523
+ const LOGIN_PATH = ${JSON.stringify(loginPath)};
4524
+ const PUBLIC = ${JSON.stringify(publicPatterns)};
4525
+ const COOKIE = ${JSON.stringify(AUTH_COOKIE_NAME)};
4526
+
4527
+ const isPublic = (uri) => {
4528
+ for (const p of PUBLIC) {
4529
+ if (p.endsWith("/*")) {
4530
+ if (uri.startsWith(p.slice(0, -1))) return true;
4531
+ } else if (p.endsWith("*")) {
4532
+ if (uri.startsWith(p.slice(0, -1))) return true;
4533
+ } else {
4534
+ if (uri === p) return true;
4535
+ }
4536
+ }
4537
+ return false;
4538
+ };
4539
+
4540
+ const verify = (cookie) => {
4541
+ if (!cookie) return false;
4542
+ const dot = cookie.indexOf(".");
4543
+ if (dot === -1) return false;
4544
+ const payload = cookie.slice(0, dot);
4545
+ const sig = cookie.slice(dot + 1);
4546
+ const expected = createHmac("sha256", SECRET).update(payload).digest("base64url");
4547
+ if (sig !== expected) return false;
4548
+ try {
4549
+ const data = JSON.parse(Buffer.from(payload, "base64url").toString("utf-8"));
4550
+ return data.exp > Math.floor(Date.now() / 1000);
4551
+ } catch { return false; }
4552
+ };
4553
+
4554
+ const parseCookies = (headers) => {
4555
+ const cookies = {};
4556
+ const cookieHeaders = headers.cookie;
4557
+ if (!cookieHeaders) return cookies;
4558
+ for (const { value } of cookieHeaders) {
4559
+ for (const pair of value.split(";")) {
4560
+ const eq = pair.indexOf("=");
4561
+ if (eq === -1) continue;
4562
+ const name = pair.slice(0, eq).trim();
4563
+ const val = pair.slice(eq + 1).trim();
4564
+ if (name) cookies[name] = val;
4565
+ }
4566
+ }
4567
+ return cookies;
4568
+ };
4569
+
4570
+ const rewrite = (uri) => {
4571
+ if (uri.endsWith("/")) return uri + "index.html";
4572
+ if (!uri.includes(".")) return uri + "/index.html";
4573
+ return uri;
4574
+ };
4575
+
4576
+ export const handler = async (event) => {
4577
+ const req = event.Records[0].cf.request;
4578
+
4579
+ if (isPublic(req.uri)) {
4580
+ req.uri = rewrite(req.uri);
4581
+ return req;
4582
+ }
4583
+
4584
+ const cookies = parseCookies(req.headers);
4585
+ if (verify(cookies[COOKIE])) {
4586
+ req.uri = rewrite(req.uri);
4587
+ return req;
4588
+ }
4589
+
4590
+ return {
4591
+ status: "302",
4592
+ statusDescription: "Found",
4593
+ headers: { location: [{ key: "Location", value: LOGIN_PATH }] },
4594
+ };
4595
+ };
4596
+ `;
4597
+ const result = yield* Effect24.tryPromise({
4598
+ try: () => esbuild.build({
4599
+ stdin: { contents: entryPoint, loader: "js", resolveDir: process.cwd() },
4600
+ bundle: true,
4601
+ platform: "node",
4602
+ target: "node22",
4603
+ write: false,
4604
+ minify: true,
4605
+ sourcemap: false,
4606
+ format: "esm",
4607
+ external: ["crypto"]
4608
+ }),
4609
+ catch: (error) => new Error(`esbuild failed (auth middleware): ${error}`)
4610
+ });
4611
+ const output = result.outputFiles?.[0];
4612
+ if (!output) throw new Error("esbuild produced no output for auth middleware");
4613
+ return output.text;
4614
+ });
4374
4615
  var FIXED_DATE2 = /* @__PURE__ */ new Date(0);
4375
4616
  var zip = (input) => Effect24.async((resume) => {
4376
4617
  const chunks = [];
@@ -4470,21 +4711,25 @@ var flattenHandlers = (discovered) => {
4470
4711
  ];
4471
4712
  };
4472
4713
 
4714
+ // src/deploy/deploy.ts
4715
+ import * as crypto5 from "crypto";
4716
+
4473
4717
  // src/deploy/resolve-config.ts
4474
4718
  import { Effect as Effect25 } from "effect";
4475
- var collectRequiredParams = (handlers, project, stage) => {
4719
+ var collectRequiredSecrets = (handlers, project, stage) => {
4476
4720
  const seen = /* @__PURE__ */ new Map();
4477
4721
  const collect = (handlerGroups) => {
4478
4722
  for (const { exports } of handlerGroups) {
4479
4723
  for (const fn13 of exports) {
4480
- for (const { propName, ssmKey } of fn13.paramEntries) {
4724
+ for (const { propName, ssmKey, generate } of fn13.secretEntries) {
4481
4725
  const ssmPath = `/${project}/${stage}/${ssmKey}`;
4482
4726
  if (!seen.has(ssmPath)) {
4483
4727
  seen.set(ssmPath, {
4484
4728
  ssmPath,
4485
4729
  propName,
4486
4730
  ssmKey,
4487
- handlerName: fn13.exportName
4731
+ handlerName: fn13.exportName,
4732
+ ...generate ? { generate } : {}
4488
4733
  });
4489
4734
  }
4490
4735
  }
@@ -4497,11 +4742,34 @@ var collectRequiredParams = (handlers, project, stage) => {
4497
4742
  collect(handlers.apiHandlers);
4498
4743
  return Array.from(seen.values());
4499
4744
  };
4500
- var checkMissingParams = (params) => Effect25.gen(function* () {
4501
- if (params.length === 0) return { existing: [], missing: [] };
4745
+ var AUTH_SSM_KEY = "_auth-secret";
4746
+ var collectAuthSecret = (handlers, project, stage) => {
4747
+ const hasAuth = (groups) => groups.some((g) => g.exports.some((fn13) => fn13.authConfig));
4748
+ if (hasAuth(handlers.apiHandlers) || hasAuth(handlers.staticSiteHandlers)) {
4749
+ return {
4750
+ ssmPath: `/${project}/${stage}/${AUTH_SSM_KEY}`,
4751
+ propName: "_authSecret",
4752
+ ssmKey: AUTH_SSM_KEY,
4753
+ handlerName: "_auth",
4754
+ generate: { type: "hex", bytes: 32 }
4755
+ };
4756
+ }
4757
+ return void 0;
4758
+ };
4759
+ var fetchAuthSecretValue = (ssmPath) => Effect25.gen(function* () {
4760
+ const result = yield* ssm_exports.make("get_parameters", {
4761
+ Names: [ssmPath],
4762
+ WithDecryption: true
4763
+ });
4764
+ const value = result.Parameters?.[0]?.Value;
4765
+ if (!value) throw new Error(`Auth secret not found at ${ssmPath}`);
4766
+ return value;
4767
+ });
4768
+ var checkMissingSecrets = (secrets) => Effect25.gen(function* () {
4769
+ if (secrets.length === 0) return { existing: [], missing: [] };
4502
4770
  const existingNames = /* @__PURE__ */ new Set();
4503
- for (let i = 0; i < params.length; i += 10) {
4504
- const batch = params.slice(i, i + 10);
4771
+ for (let i = 0; i < secrets.length; i += 10) {
4772
+ const batch = secrets.slice(i, i + 10);
4505
4773
  const result = yield* ssm_exports.make("get_parameters", {
4506
4774
  Names: batch.map((p) => p.ssmPath),
4507
4775
  WithDecryption: false
@@ -4512,7 +4780,7 @@ var checkMissingParams = (params) => Effect25.gen(function* () {
4512
4780
  }
4513
4781
  const existing = [];
4514
4782
  const missing = [];
4515
- for (const p of params) {
4783
+ for (const p of secrets) {
4516
4784
  if (existingNames.has(p.ssmPath)) {
4517
4785
  existing.push(p);
4518
4786
  } else {
@@ -4530,6 +4798,12 @@ import { toSeconds } from "effortless-aws";
4530
4798
  import { Effect as Effect26 } from "effect";
4531
4799
  import * as fs3 from "fs/promises";
4532
4800
  import * as path4 from "path";
4801
+ var formatBytes = (bytes) => {
4802
+ if (bytes < 1024) return `${bytes}B`;
4803
+ const kb = bytes / 1024;
4804
+ if (kb < 1024) return `${kb.toFixed(1)}KB`;
4805
+ return `${(kb / 1024).toFixed(2)}MB`;
4806
+ };
4533
4807
  var readSource = (input) => Effect26.gen(function* () {
4534
4808
  if ("code" in input && typeof input.code === "string") {
4535
4809
  return input.code;
@@ -4542,10 +4816,11 @@ var ensureLayerAndExternal = (input) => Effect26.gen(function* () {
4542
4816
  project: input.project,
4543
4817
  stage: input.stage,
4544
4818
  region: input.region,
4545
- projectDir: input.packageDir
4819
+ projectDir: input.packageDir,
4820
+ extraNodeModules: input.extraNodeModules
4546
4821
  });
4547
4822
  const prodDeps = layerResult ? yield* readProductionDependencies(input.packageDir) : [];
4548
- const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect26.sync(() => collectLayerPackages(input.packageDir, prodDeps)) : { packages: [], warnings: [] };
4823
+ const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect26.sync(() => collectLayerPackages(input.packageDir, prodDeps, input.extraNodeModules)) : { packages: [], warnings: [] };
4549
4824
  for (const warning of layerWarnings) {
4550
4825
  yield* Effect26.logWarning(`[layer] ${warning}`);
4551
4826
  }
@@ -4590,14 +4865,21 @@ var deployCoreLambda = ({
4590
4865
  mergedPermissions.length > 0 ? mergedPermissions : void 0,
4591
4866
  makeTags(tagCtx, "iam-role")
4592
4867
  );
4593
- const bundled = yield* bundle({
4868
+ const bundleResult = yield* bundle({
4594
4869
  ...input,
4595
4870
  exportName,
4596
4871
  ...bundleType ? { type: bundleType } : {},
4597
4872
  ...external && external.length > 0 ? { external } : {}
4598
4873
  });
4599
4874
  const staticFiles = staticGlobs && staticGlobs.length > 0 ? resolveStaticFiles(staticGlobs, input.projectDir) : void 0;
4600
- const code = yield* zip({ content: bundled, staticFiles });
4875
+ const bundleSize = Buffer.byteLength(bundleResult.code, "utf-8");
4876
+ if (bundleResult.topModules && bundleSize > 500 * 1024) {
4877
+ const top = bundleResult.topModules.slice(0, 10);
4878
+ const lines = top.map((m) => ` ${formatBytes(m.bytes).padStart(8)} ${m.path}`).join("\n");
4879
+ yield* Effect26.logWarning(`Bundle "${handlerName}" is ${formatBytes(bundleSize)} \u2014 top modules:
4880
+ ${lines}`);
4881
+ }
4882
+ const code = yield* zip({ content: bundleResult.code, staticFiles });
4601
4883
  const environment = {
4602
4884
  EFF_PROJECT: input.project,
4603
4885
  EFF_STAGE: tagCtx.stage,
@@ -4617,7 +4899,7 @@ var deployCoreLambda = ({
4617
4899
  ...layerArn ? { layers: [layerArn] } : {},
4618
4900
  environment
4619
4901
  });
4620
- return { functionArn, status, tagCtx };
4902
+ return { functionArn, status, tagCtx, bundleSize };
4621
4903
  });
4622
4904
 
4623
4905
  // src/deploy/deploy-table.ts
@@ -4639,7 +4921,7 @@ var deployTableFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsP
4639
4921
  tags: makeTags(tagCtx, "dynamodb")
4640
4922
  });
4641
4923
  const selfEnv = { EFF_DEP_SELF: `table:${tableName}`, ...depsEnv };
4642
- const { functionArn, status } = yield* deployCoreLambda({
4924
+ const { functionArn, status, bundleSize } = yield* deployCoreLambda({
4643
4925
  input,
4644
4926
  exportName,
4645
4927
  handlerName,
@@ -4667,6 +4949,7 @@ var deployTableFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsP
4667
4949
  exportName,
4668
4950
  functionArn,
4669
4951
  status,
4952
+ bundleSize,
4670
4953
  tableArn,
4671
4954
  streamArn
4672
4955
  };
@@ -4683,7 +4966,8 @@ var deployTable = (input) => Effect27.gen(function* () {
4683
4966
  project: input.project,
4684
4967
  stage: resolveStage(input.stage),
4685
4968
  region: input.region,
4686
- packageDir: input.packageDir ?? input.projectDir
4969
+ packageDir: input.packageDir ?? input.projectDir,
4970
+ extraNodeModules: input.extraNodeModules
4687
4971
  });
4688
4972
  const result = yield* deployTableFunction({
4689
4973
  input,
@@ -4712,7 +4996,8 @@ var deployAllTables = (input) => Effect27.gen(function* () {
4712
4996
  project: input.project,
4713
4997
  stage: resolveStage(input.stage),
4714
4998
  region: input.region,
4715
- packageDir: input.packageDir ?? input.projectDir
4999
+ packageDir: input.packageDir ?? input.projectDir,
5000
+ extraNodeModules: input.extraNodeModules
4716
5001
  });
4717
5002
  const results = yield* Effect27.forEach(
4718
5003
  functions,
@@ -4741,7 +5026,7 @@ import { toSeconds as toSeconds2 } from "effortless-aws";
4741
5026
  var deployApiFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect28.gen(function* () {
4742
5027
  const { exportName, config } = fn13;
4743
5028
  const handlerName = exportName;
4744
- const { functionArn, status } = yield* deployCoreLambda({
5029
+ const { functionArn, status, bundleSize } = yield* deployCoreLambda({
4745
5030
  input,
4746
5031
  exportName,
4747
5032
  handlerName,
@@ -4755,7 +5040,7 @@ var deployApiFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPer
4755
5040
  ...depsPermissions ? { depsPermissions } : {},
4756
5041
  ...staticGlobs && staticGlobs.length > 0 ? { staticGlobs } : {}
4757
5042
  });
4758
- return { exportName, functionArn, status, config, handlerName };
5043
+ return { exportName, functionArn, status, bundleSize, config, handlerName };
4759
5044
  });
4760
5045
  var deploy = (input) => Effect28.gen(function* () {
4761
5046
  const source = yield* readSource(input);
@@ -4776,7 +5061,8 @@ var deploy = (input) => Effect28.gen(function* () {
4776
5061
  project: input.project,
4777
5062
  stage: tagCtx.stage,
4778
5063
  region: input.region,
4779
- packageDir: input.packageDir ?? input.projectDir
5064
+ packageDir: input.packageDir ?? input.projectDir,
5065
+ extraNodeModules: input.extraNodeModules
4780
5066
  });
4781
5067
  const { functionArn } = yield* deployApiFunction({
4782
5068
  input,
@@ -5138,6 +5424,12 @@ var deployMiddlewareLambda = (input) => Effect31.gen(function* () {
5138
5424
  makeTags(tagCtx, "iam-role")
5139
5425
  );
5140
5426
  const bundled = yield* bundleMiddleware({ projectDir, file });
5427
+ const bundleSizeKB = Math.round(bundled.length / 1024);
5428
+ if (bundleSizeKB > 50) {
5429
+ yield* Effect31.logWarning(
5430
+ `[middleware] Bundle size is ${bundleSizeKB}KB (expected <50KB). Middleware may be pulling in unrelated dependencies via barrel imports. Use direct file imports instead (e.g. import { Auth } from "./core/auth" instead of "./core").`
5431
+ );
5432
+ }
5141
5433
  const code = yield* zip({ content: bundled });
5142
5434
  const { functionArn } = yield* ensureLambda({
5143
5435
  project,
@@ -5153,14 +5445,52 @@ var deployMiddlewareLambda = (input) => Effect31.gen(function* () {
5153
5445
  }).pipe(
5154
5446
  Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5155
5447
  );
5156
- const { versionArn } = yield* publishVersion(
5157
- `${project}-${stage}-${middlewareName}`
5158
- ).pipe(
5448
+ const edgeFunctionName = `${project}-${stage}-${middlewareName}`;
5449
+ yield* ensureEdgePermission(edgeFunctionName).pipe(
5450
+ Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5451
+ );
5452
+ const { versionArn } = yield* publishVersion(edgeFunctionName).pipe(
5159
5453
  Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5160
5454
  );
5161
5455
  yield* Effect31.logDebug(`Middleware deployed: ${versionArn}`);
5162
5456
  return { versionArn };
5163
5457
  });
5458
+ var deployAuthMiddlewareLambda = (input) => Effect31.gen(function* () {
5459
+ const { project, stage, handlerName, tagCtx, authConfig, authSecret } = input;
5460
+ const middlewareName = `${handlerName}-middleware`;
5461
+ yield* Effect31.logDebug(`Deploying auth middleware Lambda@Edge: ${middlewareName}`);
5462
+ const roleArn = yield* ensureEdgeRole(
5463
+ project,
5464
+ stage,
5465
+ middlewareName,
5466
+ makeTags(tagCtx, "iam-role")
5467
+ );
5468
+ const bundled = yield* bundleAuthMiddleware({ authConfig, secret: authSecret });
5469
+ const code = yield* zip({ content: bundled });
5470
+ const edgeFunctionName = `${project}-${stage}-${middlewareName}`;
5471
+ yield* ensureLambda({
5472
+ project,
5473
+ stage,
5474
+ name: middlewareName,
5475
+ region: "us-east-1",
5476
+ roleArn,
5477
+ code,
5478
+ memory: 128,
5479
+ timeout: 5,
5480
+ architecture: Architecture3.x86_64,
5481
+ tags: makeTags(tagCtx, "lambda")
5482
+ }).pipe(
5483
+ Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5484
+ );
5485
+ yield* ensureEdgePermission(edgeFunctionName).pipe(
5486
+ Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5487
+ );
5488
+ const { versionArn } = yield* publishVersion(edgeFunctionName).pipe(
5489
+ Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5490
+ );
5491
+ yield* Effect31.logDebug(`Auth middleware deployed: ${versionArn}`);
5492
+ return { versionArn };
5493
+ });
5164
5494
  var ERROR_PAGE_KEY = "_effortless/404.html";
5165
5495
  var generateErrorPageHtml = () => `<!DOCTYPE html>
5166
5496
  <html lang="en">
@@ -5265,7 +5595,20 @@ var deployStaticSite = (input) => Effect31.gen(function* () {
5265
5595
  const isSpa = config.spa ?? false;
5266
5596
  let urlRewriteFunctionArn;
5267
5597
  let lambdaEdgeArn;
5268
- if (hasMiddleware && input.file) {
5598
+ const hasAuth = !!(input.authSecret && input.authConfig);
5599
+ if (hasAuth) {
5600
+ const authResult = yield* deployAuthMiddlewareLambda({
5601
+ project,
5602
+ stage,
5603
+ handlerName,
5604
+ tagCtx,
5605
+ authConfig: input.authConfig,
5606
+ authSecret: input.authSecret
5607
+ }).pipe(
5608
+ Effect31.provide(clients_exports.makeClients({ iam: { region: "us-east-1" } }))
5609
+ );
5610
+ lambdaEdgeArn = authResult.versionArn;
5611
+ } else if (hasMiddleware && input.file) {
5269
5612
  const result = yield* deployMiddlewareLambda({
5270
5613
  projectDir,
5271
5614
  project,
@@ -5395,7 +5738,7 @@ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, d
5395
5738
  EFF_QUEUE_ARN: queueArn,
5396
5739
  ...depsEnv
5397
5740
  };
5398
- const { functionArn, status } = yield* deployCoreLambda({
5741
+ const { functionArn, status, bundleSize } = yield* deployCoreLambda({
5399
5742
  input,
5400
5743
  exportName,
5401
5744
  handlerName,
@@ -5422,6 +5765,7 @@ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, d
5422
5765
  exportName,
5423
5766
  functionArn,
5424
5767
  status,
5768
+ bundleSize,
5425
5769
  queueUrl,
5426
5770
  queueArn
5427
5771
  };
@@ -5456,7 +5800,7 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
5456
5800
  };
5457
5801
  }
5458
5802
  const selfEnv = { EFF_DEP_SELF: `bucket:${bucketName}`, ...depsEnv };
5459
- const { functionArn, status } = yield* deployCoreLambda({
5803
+ const { functionArn, status, bundleSize } = yield* deployCoreLambda({
5460
5804
  input,
5461
5805
  exportName,
5462
5806
  handlerName,
@@ -5484,6 +5828,7 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
5484
5828
  exportName,
5485
5829
  functionArn,
5486
5830
  status,
5831
+ bundleSize,
5487
5832
  bucketName,
5488
5833
  bucketArn
5489
5834
  };
@@ -5564,10 +5909,17 @@ var createLiveProgress = (manifest) => {
5564
5909
  const sec = ((Date.now() - startTime) / 1e3).toFixed(1);
5565
5910
  return c.dim(`${sec}s`);
5566
5911
  };
5567
- return (name, type, status) => Effect35.sync(() => {
5912
+ const formatSize = (bytes) => {
5913
+ if (bytes < 1024) return `${bytes}B`;
5914
+ const kb = bytes / 1024;
5915
+ if (kb < 1024) return `${kb.toFixed(0)}KB`;
5916
+ return `${(kb / 1024).toFixed(1)}MB`;
5917
+ };
5918
+ return (name, type, status, bundleSize) => Effect35.sync(() => {
5568
5919
  const key = `${name}:${type}`;
5569
5920
  results.set(key, status);
5570
- const line = ` ${name} ${c.dim(`(${type})`)} ${statusLabel(status)} ${formatDuration()}`;
5921
+ const sizeInfo = bundleSize ? ` ${c.dim(formatSize(bundleSize))}` : "";
5922
+ const line = ` ${name} ${c.dim(`(${type})`)} ${statusLabel(status)}${sizeInfo} ${formatDuration()}`;
5571
5923
  if (isTTY) {
5572
5924
  const idx = lineIndex.get(key) ?? 0;
5573
5925
  const up = manifest.length - idx;
@@ -5576,7 +5928,7 @@ var createLiveProgress = (manifest) => {
5576
5928
  clearInterval(timer);
5577
5929
  }
5578
5930
  } else {
5579
- process.stdout.write(` ${c.dim(`[${results.size}/${manifest.length}]`)} ${name} ${c.dim(`(${type})`)} ${statusLabel(status)} ${formatDuration()}
5931
+ process.stdout.write(` ${c.dim(`[${results.size}/${manifest.length}]`)} ${name} ${c.dim(`(${type})`)} ${statusLabel(status)}${sizeInfo} ${formatDuration()}
5580
5932
  `);
5581
5933
  }
5582
5934
  });
@@ -5587,7 +5939,8 @@ var prepareLayer = (input) => Effect35.gen(function* () {
5587
5939
  project: input.project,
5588
5940
  stage: input.stage,
5589
5941
  region: input.region,
5590
- projectDir: input.packageDir
5942
+ projectDir: input.packageDir,
5943
+ extraNodeModules: input.extraNodeModules
5591
5944
  }).pipe(
5592
5945
  Effect35.provide(
5593
5946
  clients_exports.makeClients({
@@ -5596,7 +5949,7 @@ var prepareLayer = (input) => Effect35.gen(function* () {
5596
5949
  )
5597
5950
  );
5598
5951
  const prodDeps = layerResult ? yield* readProductionDependencies(input.packageDir) : [];
5599
- const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect35.sync(() => collectLayerPackages(input.packageDir, prodDeps)) : { packages: [], warnings: [] };
5952
+ const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect35.sync(() => collectLayerPackages(input.packageDir, prodDeps, input.extraNodeModules)) : { packages: [], warnings: [] };
5600
5953
  for (const warning of layerWarnings) {
5601
5954
  yield* Effect35.logWarning(`[layer] ${warning}`);
5602
5955
  }
@@ -5742,10 +6095,20 @@ var SSM_PERMISSIONS = [
5742
6095
  "ssm:GetParameter",
5743
6096
  "ssm:GetParameters"
5744
6097
  ];
5745
- var resolveParams = (paramEntries, project, stage) => {
5746
- if (paramEntries.length === 0) return void 0;
6098
+ var executeGenerateSpec = (spec) => {
6099
+ switch (spec.type) {
6100
+ case "hex":
6101
+ return crypto5.randomBytes(spec.bytes).toString("hex");
6102
+ case "base64":
6103
+ return crypto5.randomBytes(spec.bytes).toString("base64url");
6104
+ case "uuid":
6105
+ return crypto5.randomUUID();
6106
+ }
6107
+ };
6108
+ var resolveSecrets = (secretEntries, project, stage) => {
6109
+ if (secretEntries.length === 0) return void 0;
5747
6110
  const paramsEnv = {};
5748
- for (const { propName, ssmKey } of paramEntries) {
6111
+ for (const { propName, ssmKey } of secretEntries) {
5749
6112
  paramsEnv[`EFF_PARAM_${propName}`] = `/${project}/${stage}/${ssmKey}`;
5750
6113
  }
5751
6114
  return { paramsEnv, paramsPermissions: SSM_PERMISSIONS };
@@ -5764,10 +6127,10 @@ var makeDeployInput = (ctx, file) => ({
5764
6127
  region: ctx.input.region,
5765
6128
  ...ctx.input.stage ? { stage: ctx.input.stage } : {}
5766
6129
  });
5767
- var resolveHandlerEnv = (depsKeys, paramEntries, ctx) => {
6130
+ var resolveHandlerEnv = (depsKeys, secretEntries, ctx) => {
5768
6131
  const resolved = mergeResolved(
5769
6132
  resolveDeps(depsKeys, ctx.tableNameMap, ctx.bucketNameMap, ctx.mailerDomainMap, ctx.queueNameMap),
5770
- resolveParams(paramEntries, ctx.input.project, ctx.stage)
6133
+ resolveSecrets(secretEntries, ctx.input.project, ctx.stage)
5771
6134
  );
5772
6135
  return {
5773
6136
  depsEnv: resolved?.depsEnv ?? {},
@@ -5781,7 +6144,7 @@ var buildTableTasks = (ctx, handlers, results) => {
5781
6144
  for (const fn13 of exports) {
5782
6145
  tasks.push(
5783
6146
  Effect35.gen(function* () {
5784
- const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
6147
+ const env = resolveHandlerEnv(fn13.depsKeys, fn13.secretEntries, ctx);
5785
6148
  const result = yield* deployTableFunction({
5786
6149
  input: makeDeployInput(ctx, file),
5787
6150
  fn: fn13,
@@ -5792,7 +6155,7 @@ var buildTableTasks = (ctx, handlers, results) => {
5792
6155
  ...fn13.staticGlobs.length > 0 ? { staticGlobs: fn13.staticGlobs } : {}
5793
6156
  }).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, dynamodb: { region } })));
5794
6157
  results.push(result);
5795
- yield* ctx.logComplete(fn13.exportName, "table", result.status);
6158
+ yield* ctx.logComplete(fn13.exportName, "table", result.status, result.bundleSize);
5796
6159
  })
5797
6160
  );
5798
6161
  }
@@ -5837,6 +6200,12 @@ var buildStaticSiteTasks = (ctx, handlers, results, apiOriginDomain) => {
5837
6200
  for (const fn13 of exports) {
5838
6201
  tasks.push(
5839
6202
  Effect35.gen(function* () {
6203
+ let authSecretValue;
6204
+ if (fn13.authConfig && ctx.authSecretPath) {
6205
+ authSecretValue = yield* fetchAuthSecretValue(ctx.authSecretPath).pipe(
6206
+ Effect35.provide(clients_exports.makeClients({ ssm: { region: ctx.input.region } }))
6207
+ );
6208
+ }
5840
6209
  const result = yield* deployStaticSite({
5841
6210
  projectDir: ctx.input.projectDir,
5842
6211
  project: ctx.input.project,
@@ -5845,7 +6214,8 @@ var buildStaticSiteTasks = (ctx, handlers, results, apiOriginDomain) => {
5845
6214
  fn: fn13,
5846
6215
  verbose: ctx.input.verbose,
5847
6216
  ...fn13.hasHandler ? { file } : {},
5848
- ...apiOriginDomain ? { apiOriginDomain } : {}
6217
+ ...apiOriginDomain ? { apiOriginDomain } : {},
6218
+ ...authSecretValue && fn13.authConfig ? { authSecret: authSecretValue, authConfig: fn13.authConfig } : {}
5849
6219
  }).pipe(Effect35.provide(clients_exports.makeClients({
5850
6220
  s3: { region },
5851
6221
  cloudfront: { region: "us-east-1" },
@@ -5867,7 +6237,7 @@ var buildFifoQueueTasks = (ctx, handlers, results) => {
5867
6237
  for (const fn13 of exports) {
5868
6238
  tasks.push(
5869
6239
  Effect35.gen(function* () {
5870
- const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
6240
+ const env = resolveHandlerEnv(fn13.depsKeys, fn13.secretEntries, ctx);
5871
6241
  const result = yield* deployFifoQueueFunction({
5872
6242
  input: makeDeployInput(ctx, file),
5873
6243
  fn: fn13,
@@ -5878,7 +6248,7 @@ var buildFifoQueueTasks = (ctx, handlers, results) => {
5878
6248
  ...fn13.staticGlobs.length > 0 ? { staticGlobs: fn13.staticGlobs } : {}
5879
6249
  }).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, sqs: { region } })));
5880
6250
  results.push(result);
5881
- yield* ctx.logComplete(fn13.exportName, "queue", result.status);
6251
+ yield* ctx.logComplete(fn13.exportName, "queue", result.status, result.bundleSize);
5882
6252
  })
5883
6253
  );
5884
6254
  }
@@ -5892,7 +6262,7 @@ var buildBucketTasks = (ctx, handlers, results) => {
5892
6262
  for (const fn13 of exports) {
5893
6263
  tasks.push(
5894
6264
  Effect35.gen(function* () {
5895
- const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
6265
+ const env = resolveHandlerEnv(fn13.depsKeys, fn13.secretEntries, ctx);
5896
6266
  const result = yield* deployBucketFunction({
5897
6267
  input: makeDeployInput(ctx, file),
5898
6268
  fn: fn13,
@@ -5904,7 +6274,7 @@ var buildBucketTasks = (ctx, handlers, results) => {
5904
6274
  }).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, s3: { region } })));
5905
6275
  results.push(result);
5906
6276
  const status = result.status === "resource-only" ? "created" : result.status;
5907
- yield* ctx.logComplete(fn13.exportName, "bucket", status);
6277
+ yield* ctx.logComplete(fn13.exportName, "bucket", status, result.bundleSize);
5908
6278
  })
5909
6279
  );
5910
6280
  }
@@ -5939,8 +6309,12 @@ var buildApiTasks = (ctx, handlers, results) => {
5939
6309
  for (const fn13 of exports) {
5940
6310
  tasks.push(
5941
6311
  Effect35.gen(function* () {
5942
- const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
5943
- const { exportName, functionArn, status, handlerName } = yield* deployApiFunction({
6312
+ const env = resolveHandlerEnv(fn13.depsKeys, fn13.secretEntries, ctx);
6313
+ if (fn13.authConfig && ctx.authSecretPath) {
6314
+ env.depsEnv["EFF_AUTH_SECRET"] = ctx.authSecretPath;
6315
+ env.depsPermissions = [...env.depsPermissions, "ssm:GetParameter", "ssm:GetParameters"];
6316
+ }
6317
+ const { exportName, functionArn, status, bundleSize, handlerName } = yield* deployApiFunction({
5944
6318
  input: makeDeployInput(ctx, file),
5945
6319
  fn: fn13,
5946
6320
  ...ctx.layerArn ? { layerArn: ctx.layerArn } : {},
@@ -5957,7 +6331,7 @@ var buildApiTasks = (ctx, handlers, results) => {
5957
6331
  Effect35.provide(clients_exports.makeClients({ lambda: { region } }))
5958
6332
  );
5959
6333
  results.push({ exportName, url: functionUrl, functionArn });
5960
- yield* ctx.logComplete(exportName, "api", status);
6334
+ yield* ctx.logComplete(exportName, "api", status, bundleSize);
5961
6335
  })
5962
6336
  );
5963
6337
  }
@@ -5994,16 +6368,33 @@ var deployProject = (input) => Effect35.gen(function* () {
5994
6368
  yield* Console2.log(`
5995
6369
  ${c.dim("Handlers:")} ${parts.join(", ")}`);
5996
6370
  const discovered = { tableHandlers, appHandlers, staticSiteHandlers, fifoQueueHandlers, bucketHandlers, mailerHandlers, apiHandlers };
5997
- const requiredParams = collectRequiredParams(discovered, input.project, stage);
5998
- if (requiredParams.length > 0) {
5999
- const { missing } = yield* checkMissingParams(requiredParams).pipe(
6371
+ const requiredSecrets = collectRequiredSecrets(discovered, input.project, stage);
6372
+ const authSecret = collectAuthSecret(discovered, input.project, stage);
6373
+ if (authSecret) requiredSecrets.push(authSecret);
6374
+ if (requiredSecrets.length > 0) {
6375
+ const { missing } = yield* checkMissingSecrets(requiredSecrets).pipe(
6000
6376
  Effect35.provide(clients_exports.makeClients({ ssm: { region: input.region } }))
6001
6377
  );
6002
- if (missing.length > 0) {
6378
+ const withGenerators = missing.filter((m) => m.generate);
6379
+ const manualOnly = missing.filter((m) => !m.generate);
6380
+ if (withGenerators.length > 0) {
6381
+ for (const entry of withGenerators) {
6382
+ const value = executeGenerateSpec(entry.generate);
6383
+ yield* ssm_exports.make("put_parameter", {
6384
+ Name: entry.ssmPath,
6385
+ Value: value,
6386
+ Type: "SecureString"
6387
+ }).pipe(Effect35.provide(clients_exports.makeClients({ ssm: { region: input.region } })));
6388
+ yield* Effect35.logDebug(`Auto-created SSM parameter: ${entry.ssmPath}`);
6389
+ }
6003
6390
  yield* Console2.log(`
6004
- ${c.yellow("\u26A0")} Missing ${missing.length} SSM parameter(s):
6391
+ ${c.green("\u2713")} Auto-created ${withGenerators.length} secret(s)`);
6392
+ }
6393
+ if (manualOnly.length > 0) {
6394
+ yield* Console2.log(`
6395
+ ${c.yellow("\u26A0")} Missing ${manualOnly.length} SSM parameter(s):
6005
6396
  `);
6006
- for (const p of missing) {
6397
+ for (const p of manualOnly) {
6007
6398
  yield* Console2.log(` ${c.dim(p.handlerName)} \u2192 ${c.yellow(p.ssmPath)}`);
6008
6399
  }
6009
6400
  yield* Console2.log(`
@@ -6027,7 +6418,8 @@ var deployProject = (input) => Effect35.gen(function* () {
6027
6418
  project: input.project,
6028
6419
  stage,
6029
6420
  region: input.region,
6030
- packageDir: input.packageDir ?? input.projectDir
6421
+ packageDir: input.packageDir ?? input.projectDir,
6422
+ extraNodeModules: input.extraNodeModules
6031
6423
  }) : { layerArn: void 0, layerVersion: void 0, layerStatus: void 0, external: [] };
6032
6424
  if (layerArn && layerStatus) {
6033
6425
  const status = layerStatus === "cached" ? c.dim("cached") : c.green("created");
@@ -6062,7 +6454,8 @@ var deployProject = (input) => Effect35.gen(function* () {
6062
6454
  bucketNameMap,
6063
6455
  mailerDomainMap,
6064
6456
  queueNameMap,
6065
- logComplete
6457
+ logComplete,
6458
+ ...authSecret ? { authSecretPath: authSecret.ssmPath } : {}
6066
6459
  };
6067
6460
  const tableResults = [];
6068
6461
  const appResults = [];
@@ -6265,7 +6658,8 @@ var deployCommand = Command.make(
6265
6658
  stage: finalStage,
6266
6659
  region: finalRegion,
6267
6660
  noSites,
6268
- verbose
6661
+ verbose,
6662
+ extraNodeModules: projectDir !== cwd ? [path10.join(projectDir, "node_modules")] : void 0
6269
6663
  });
6270
6664
  const total = results.tableResults.length + results.appResults.length + results.staticSiteResults.length + results.apiResults.length;
6271
6665
  yield* Console3.log(`
@@ -6359,7 +6753,8 @@ Deployed ${tableResults.length} table handler(s):`));
6359
6753
  project,
6360
6754
  stage: finalStage,
6361
6755
  region: finalRegion,
6362
- exportName: foundExport
6756
+ exportName: foundExport,
6757
+ extraNodeModules: projectDir !== cwd ? [path10.join(projectDir, "node_modules")] : void 0
6363
6758
  };
6364
6759
  if (handlerType === "table") {
6365
6760
  const result = yield* deployTable(input);
@@ -6464,9 +6859,9 @@ var STATUS_COLORS = {
6464
6859
  var formatStatus = (status) => {
6465
6860
  return STATUS_COLORS[status](status.padEnd(10));
6466
6861
  };
6467
- var formatRoute = (method, path12) => {
6468
- if (method && path12) return `${method.padEnd(5)} ${path12}`;
6469
- if (path12) return path12;
6862
+ var formatRoute = (method, path13) => {
6863
+ if (method && path13) return `${method.padEnd(5)} ${path13}`;
6864
+ if (path13) return path13;
6470
6865
  return "";
6471
6866
  };
6472
6867
  var formatEntry = (entry) => {
@@ -7112,15 +7507,16 @@ var layerCommand = Command5.make(
7112
7507
  "layer",
7113
7508
  { build: buildOption, output: outputOption, verbose: verboseOption },
7114
7509
  ({ build: build3, output, verbose }) => Effect43.gen(function* () {
7115
- const { config, cwd } = yield* ProjectConfig;
7510
+ const { config, cwd, projectDir } = yield* ProjectConfig;
7511
+ const extraNodeModules = projectDir !== cwd ? [path11.join(projectDir, "node_modules")] : void 0;
7116
7512
  if (build3) {
7117
- yield* buildLayer(cwd, output, verbose);
7513
+ yield* buildLayer(cwd, output, verbose, extraNodeModules);
7118
7514
  } else {
7119
- yield* showLayerInfo(cwd, config?.name, verbose);
7515
+ yield* showLayerInfo(cwd, config?.name, verbose, extraNodeModules);
7120
7516
  }
7121
7517
  }).pipe(Effect43.provide(ProjectConfig.Live))
7122
7518
  ).pipe(Command5.withDescription("Inspect or locally build the shared Lambda dependency layer from package.json"));
7123
- var showLayerInfo = (projectDir, projectName, verbose) => Effect43.gen(function* () {
7519
+ var showLayerInfo = (projectDir, projectName, verbose, extraNodeModules) => Effect43.gen(function* () {
7124
7520
  yield* Console7.log(`
7125
7521
  ${c.bold("=== Layer Packages Preview ===")}
7126
7522
  `);
@@ -7143,7 +7539,7 @@ ${c.bold("=== Layer Packages Preview ===")}
7143
7539
  for (const dep of prodDeps) {
7144
7540
  yield* Console7.log(` ${dep}`);
7145
7541
  }
7146
- const hash = yield* computeLockfileHash(projectDir).pipe(
7542
+ const hash = yield* computeLockfileHash(projectDir, extraNodeModules).pipe(
7147
7543
  Effect43.catchAll(() => Effect43.succeed(null))
7148
7544
  );
7149
7545
  if (hash) {
@@ -7152,7 +7548,7 @@ Lockfile hash: ${hash}`);
7152
7548
  } else {
7153
7549
  yield* Console7.log("\nNo lockfile found (package-lock.json, pnpm-lock.yaml, or yarn.lock)");
7154
7550
  }
7155
- const { packages: allPackages, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps));
7551
+ const { packages: allPackages, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps, extraNodeModules));
7156
7552
  if (layerWarnings.length > 0) {
7157
7553
  yield* Console7.log(c.yellow(`
7158
7554
  Warnings (${layerWarnings.length}):`));
@@ -7181,7 +7577,7 @@ Total packages for layer ${c.dim(`(${allPackages.length})`)}:`);
7181
7577
  Layer name: ${projectName}-deps`);
7182
7578
  }
7183
7579
  });
7184
- var buildLayer = (projectDir, output, verbose) => Effect43.gen(function* () {
7580
+ var buildLayer = (projectDir, output, verbose, extraNodeModules) => Effect43.gen(function* () {
7185
7581
  const outputDir = path11.isAbsolute(output) ? output : path11.resolve(projectDir, output);
7186
7582
  const layerDir = path11.join(outputDir, "nodejs", "node_modules");
7187
7583
  const layerRoot = path11.join(outputDir, "nodejs");
@@ -7211,12 +7607,12 @@ ${c.bold("=== Building Layer Locally ===")}
7211
7607
  for (const dep of prodDeps) {
7212
7608
  yield* Console7.log(` ${dep}`);
7213
7609
  }
7214
- const hash = yield* computeLockfileHash(projectDir).pipe(
7610
+ const hash = yield* computeLockfileHash(projectDir, extraNodeModules).pipe(
7215
7611
  Effect43.catchAll(() => Effect43.succeed("unknown"))
7216
7612
  );
7217
7613
  yield* Console7.log(`
7218
7614
  Lockfile hash: ${hash}`);
7219
- const { packages: allPackages, resolvedPaths, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps));
7615
+ const { packages: allPackages, resolvedPaths, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps, extraNodeModules));
7220
7616
  if (layerWarnings.length > 0) {
7221
7617
  yield* Console7.log(`
7222
7618
  Warnings (${layerWarnings.length}):`);
@@ -7283,7 +7679,7 @@ var loadRequiredParams = (projectOpt, stage, region) => Effect44.gen(function* (
7283
7679
  const handlers = discoverHandlers(files);
7284
7680
  const finalStage = config?.stage ?? stage;
7285
7681
  const finalRegion = config?.region ?? region;
7286
- const params = collectRequiredParams(handlers, project, finalStage);
7682
+ const params = collectRequiredSecrets(handlers, project, finalStage);
7287
7683
  return { params, project, stage: finalStage, region: finalRegion };
7288
7684
  });
7289
7685
  var listCommand = Command6.make(
@@ -7296,7 +7692,7 @@ var listCommand = Command6.make(
7296
7692
  yield* Console8.log("No config parameters declared in handlers.");
7297
7693
  return;
7298
7694
  }
7299
- const { existing, missing } = yield* checkMissingParams(params).pipe(
7695
+ const { existing, missing } = yield* checkMissingSecrets(params).pipe(
7300
7696
  Effect44.provide(clients_exports.makeClients({ ssm: { region: ctx.region } }))
7301
7697
  );
7302
7698
  yield* Console8.log(`
@@ -7367,7 +7763,7 @@ var configRootCommand = Command6.make(
7367
7763
  yield* Console8.log("No config parameters declared in handlers.");
7368
7764
  return;
7369
7765
  }
7370
- const { missing } = yield* checkMissingParams(params).pipe(
7766
+ const { missing } = yield* checkMissingSecrets(params).pipe(
7371
7767
  Effect44.provide(clients_exports.makeClients({ ssm: { region: ctx.region } }))
7372
7768
  );
7373
7769
  if (missing.length === 0) {
@@ -7416,25 +7812,189 @@ ${c.bold("Missing parameters")} ${c.dim(`(${ctx.project} / ${ctx.stage})`)}
7416
7812
  );
7417
7813
  var configCommand = configRootCommand;
7418
7814
 
7815
+ // src/cli/commands/build.ts
7816
+ import { Args as Args4, Command as Command7, Options as Options5 } from "@effect/cli";
7817
+ import { Effect as Effect45, Console as Console9, Option as Option6 } from "effect";
7818
+ import * as path12 from "path";
7819
+ import * as fs8 from "fs";
7820
+ var buildFileArg = Args4.file({ name: "file", exists: "yes" }).pipe(
7821
+ Args4.withDescription("Handler file to build"),
7822
+ Args4.optional
7823
+ );
7824
+ var buildAllOption = Options5.boolean("all").pipe(
7825
+ Options5.withDescription("Build all exports from file")
7826
+ );
7827
+ var buildTableOption = Options5.boolean("table").pipe(
7828
+ Options5.withDescription("Build as table handler (defineTable)")
7829
+ );
7830
+ var buildCommand = Command7.make(
7831
+ "build",
7832
+ { file: buildFileArg, all: buildAllOption, table: buildTableOption, output: outputOption, verbose: verboseOption },
7833
+ ({ file, all, table, output, verbose }) => Effect45.gen(function* () {
7834
+ const { config, projectDir, cwd } = yield* ProjectConfig;
7835
+ const outputDir = path12.isAbsolute(output) ? output : path12.resolve(projectDir, output);
7836
+ const extraNodeModules = projectDir !== cwd ? [path12.join(projectDir, "node_modules")] : void 0;
7837
+ if (!fs8.existsSync(outputDir)) {
7838
+ fs8.mkdirSync(outputDir, { recursive: true });
7839
+ }
7840
+ const prodDeps = yield* readProductionDependencies(projectDir).pipe(
7841
+ Effect45.catchAll(() => Effect45.succeed([]))
7842
+ );
7843
+ const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect45.sync(() => collectLayerPackages(projectDir, prodDeps, extraNodeModules)) : { packages: [], warnings: [] };
7844
+ for (const warning of layerWarnings) {
7845
+ yield* Effect45.logWarning(`[layer] ${warning}`);
7846
+ }
7847
+ if (external.length > 0) {
7848
+ yield* Console9.log(`Using ${external.length} external packages (from layer)
7849
+ `);
7850
+ }
7851
+ yield* Option6.match(file, {
7852
+ onNone: () => Effect45.gen(function* () {
7853
+ const patterns = getPatternsFromConfig(config);
7854
+ if (!patterns) {
7855
+ yield* Console9.error("Error: No file specified and no 'handlers' patterns in config");
7856
+ return;
7857
+ }
7858
+ const files = findHandlerFiles(patterns, projectDir);
7859
+ const discovered = discoverHandlers(files);
7860
+ let builtCount = 0;
7861
+ for (const { file: handlerFile, exports } of discovered.apiHandlers) {
7862
+ const relativePath = path12.relative(projectDir, handlerFile);
7863
+ const baseName = path12.basename(handlerFile, path12.extname(handlerFile));
7864
+ for (const { exportName } of exports) {
7865
+ const outputName = exportName === "default" ? baseName : `${baseName}-${exportName}`;
7866
+ const outputPath = path12.join(outputDir, `${outputName}.mjs`);
7867
+ yield* Console9.log(`Building ${c.cyan("[api]")} ${c.bold(relativePath)}:${exportName}...`);
7868
+ const result = yield* bundle({
7869
+ projectDir,
7870
+ file: handlerFile,
7871
+ exportName,
7872
+ type: "api",
7873
+ ...external.length > 0 ? { external } : {}
7874
+ });
7875
+ fs8.writeFileSync(outputPath, result.code);
7876
+ const size = (Buffer.byteLength(result.code) / 1024).toFixed(1);
7877
+ yield* Console9.log(` -> ${outputPath} ${c.dim(`(${size} KB)`)}`);
7878
+ if (result.topModules) {
7879
+ for (const m of result.topModules.slice(0, 5)) {
7880
+ yield* Console9.log(` ${c.dim(`${(m.bytes / 1024).toFixed(1)} KB`)} ${c.dim(m.path)}`);
7881
+ }
7882
+ }
7883
+ builtCount++;
7884
+ }
7885
+ }
7886
+ for (const { file: handlerFile, exports } of discovered.tableHandlers) {
7887
+ const relativePath = path12.relative(projectDir, handlerFile);
7888
+ const baseName = path12.basename(handlerFile, path12.extname(handlerFile));
7889
+ for (const { exportName } of exports) {
7890
+ const outputName = exportName === "default" ? baseName : `${baseName}-${exportName}`;
7891
+ const outputPath = path12.join(outputDir, `${outputName}.mjs`);
7892
+ yield* Console9.log(`Building ${c.cyan("[table]")} ${c.bold(relativePath)}:${exportName}...`);
7893
+ const result = yield* bundle({
7894
+ projectDir,
7895
+ file: handlerFile,
7896
+ exportName,
7897
+ type: "table",
7898
+ ...external.length > 0 ? { external } : {}
7899
+ });
7900
+ fs8.writeFileSync(outputPath, result.code);
7901
+ const size = (Buffer.byteLength(result.code) / 1024).toFixed(1);
7902
+ yield* Console9.log(` -> ${outputPath} ${c.dim(`(${size} KB)`)}`);
7903
+ if (result.topModules) {
7904
+ for (const m of result.topModules.slice(0, 5)) {
7905
+ yield* Console9.log(` ${c.dim(`${(m.bytes / 1024).toFixed(1)} KB`)} ${c.dim(m.path)}`);
7906
+ }
7907
+ }
7908
+ builtCount++;
7909
+ }
7910
+ }
7911
+ yield* Console9.log(c.green(`
7912
+ Built ${builtCount} handler(s) to ${outputDir}`));
7913
+ }),
7914
+ onSome: (filePath) => Effect45.gen(function* () {
7915
+ const fullPath = path12.isAbsolute(filePath) ? filePath : path12.resolve(projectDir, filePath);
7916
+ const source = fs8.readFileSync(fullPath, "utf-8");
7917
+ const baseName = path12.basename(fullPath, path12.extname(fullPath));
7918
+ if (table) {
7919
+ const configs = extractTableConfigs(source);
7920
+ if (configs.length === 0) {
7921
+ yield* Console9.error("No defineTable handlers found in file");
7922
+ return;
7923
+ }
7924
+ const toBundle = all ? configs : [configs[0]];
7925
+ for (const { exportName } of toBundle) {
7926
+ const outputName = exportName === "default" ? baseName : `${baseName}-${exportName}`;
7927
+ const outputPath = path12.join(outputDir, `${outputName}.mjs`);
7928
+ yield* Console9.log(`Building ${c.cyan("[table]")} ${c.bold(exportName)}...`);
7929
+ const result = yield* bundle({
7930
+ projectDir,
7931
+ file: fullPath,
7932
+ exportName,
7933
+ type: "table",
7934
+ ...external.length > 0 ? { external } : {}
7935
+ });
7936
+ fs8.writeFileSync(outputPath, result.code);
7937
+ const size = (Buffer.byteLength(result.code) / 1024).toFixed(1);
7938
+ yield* Console9.log(` -> ${outputPath} ${c.dim(`(${size} KB)`)}`);
7939
+ if (result.topModules) {
7940
+ for (const m of result.topModules.slice(0, 5)) {
7941
+ yield* Console9.log(` ${c.dim(`${(m.bytes / 1024).toFixed(1)} KB`)} ${c.dim(m.path)}`);
7942
+ }
7943
+ }
7944
+ }
7945
+ } else {
7946
+ const configs = extractApiConfigs(source);
7947
+ if (configs.length === 0) {
7948
+ yield* Console9.error("No defineApi handlers found in file");
7949
+ return;
7950
+ }
7951
+ const toBundle = all ? configs : [configs[0]];
7952
+ for (const { exportName } of toBundle) {
7953
+ const outputName = exportName === "default" ? baseName : `${baseName}-${exportName}`;
7954
+ const outputPath = path12.join(outputDir, `${outputName}.mjs`);
7955
+ yield* Console9.log(`Building ${c.cyan("[api]")} ${c.bold(exportName)}...`);
7956
+ const result = yield* bundle({
7957
+ projectDir,
7958
+ file: fullPath,
7959
+ exportName,
7960
+ ...external.length > 0 ? { external } : {}
7961
+ });
7962
+ fs8.writeFileSync(outputPath, result.code);
7963
+ const size = (Buffer.byteLength(result.code) / 1024).toFixed(1);
7964
+ yield* Console9.log(` -> ${outputPath} ${c.dim(`(${size} KB)`)}`);
7965
+ if (result.topModules) {
7966
+ for (const m of result.topModules.slice(0, 5)) {
7967
+ yield* Console9.log(` ${c.dim(`${(m.bytes / 1024).toFixed(1)} KB`)} ${c.dim(m.path)}`);
7968
+ }
7969
+ }
7970
+ }
7971
+ }
7972
+ yield* Console9.log(`
7973
+ Output directory: ${outputDir}`);
7974
+ })
7975
+ });
7976
+ }).pipe(Effect45.provide(ProjectConfig.Live))
7977
+ ).pipe(Command7.withDescription("Build handler bundles locally (for debugging)"));
7978
+
7419
7979
  // src/cli/update-check.ts
7420
7980
  import { homedir as homedir2 } from "os";
7421
- import { join as join9 } from "path";
7422
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
7981
+ import { join as join11 } from "path";
7982
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
7423
7983
  var PACKAGE_NAME = "@effortless-aws/cli";
7424
7984
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
7425
- var CACHE_DIR = join9(homedir2(), ".effortless-aws");
7426
- var CACHE_FILE = join9(CACHE_DIR, "update-check.json");
7985
+ var CACHE_DIR = join11(homedir2(), ".effortless-aws");
7986
+ var CACHE_FILE = join11(CACHE_DIR, "update-check.json");
7427
7987
  function readCache() {
7428
7988
  try {
7429
- return JSON.parse(readFileSync5(CACHE_FILE, "utf-8"));
7989
+ return JSON.parse(readFileSync6(CACHE_FILE, "utf-8"));
7430
7990
  } catch {
7431
7991
  return void 0;
7432
7992
  }
7433
7993
  }
7434
7994
  function writeCache(data) {
7435
7995
  try {
7436
- mkdirSync2(CACHE_DIR, { recursive: true });
7437
- writeFileSync2(CACHE_FILE, JSON.stringify(data));
7996
+ mkdirSync3(CACHE_DIR, { recursive: true });
7997
+ writeFileSync3(CACHE_FILE, JSON.stringify(data));
7438
7998
  } catch {
7439
7999
  }
7440
8000
  }
@@ -7496,18 +8056,18 @@ async function checkForUpdate(currentVersion) {
7496
8056
  // src/cli/index.ts
7497
8057
  var require2 = createRequire2(import.meta.url);
7498
8058
  var { version } = require2("../../package.json");
7499
- var mainCommand = Command7.make("eff").pipe(
7500
- Command7.withSubcommands([deployCommand, statusCommand, logsCommand, cleanupCommand, layerCommand, configCommand]),
7501
- Command7.withDescription("Code-first AWS Lambda framework")
8059
+ var mainCommand = Command8.make("eff").pipe(
8060
+ Command8.withSubcommands([deployCommand, statusCommand, logsCommand, cleanupCommand, layerCommand, configCommand, buildCommand]),
8061
+ Command8.withDescription("Code-first AWS Lambda framework")
7502
8062
  );
7503
- var cli = Command7.run(mainCommand, {
8063
+ var cli = Command8.run(mainCommand, {
7504
8064
  name: "effortless",
7505
8065
  version
7506
8066
  });
7507
8067
  var updateCheck = checkForUpdate(version);
7508
8068
  cli(process.argv).pipe(
7509
- Effect45.provide(NodeContext.layer),
7510
- Effect45.provide(CliConfig.layer({ showBuiltIns: false, showTypes: false })),
7511
- Effect45.tap(() => Effect45.promise(() => updateCheck)),
8069
+ Effect46.provide(NodeContext.layer),
8070
+ Effect46.provide(CliConfig.layer({ showBuiltIns: false, showTypes: false })),
8071
+ Effect46.tap(() => Effect46.promise(() => updateCheck)),
7512
8072
  NodeRuntime.runMain
7513
8073
  );