@effortless-aws/cli 0.8.0 → 0.9.1

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 +818 -255
  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,9 @@ 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])];
4436
+ const format = input.format ?? "esm";
4320
4437
  const result = yield* Effect24.tryPromise({
4321
4438
  try: () => esbuild.build({
4322
4439
  stdin: {
@@ -4330,8 +4447,11 @@ var bundle = (input) => Effect24.gen(function* () {
4330
4447
  write: false,
4331
4448
  minify: false,
4332
4449
  sourcemap: false,
4333
- format: input.format ?? "esm",
4334
- external: allExternals
4450
+ format,
4451
+ external: allExternals,
4452
+ metafile: true,
4453
+ // CJS packages bundled into ESM need a `require` function for Node.js builtins
4454
+ ...format === "esm" ? { banner: { js: "import { createRequire } from 'module'; const require = createRequire(import.meta.url);" } } : {}
4335
4455
  }),
4336
4456
  catch: (error) => new Error(`esbuild failed: ${error}`)
4337
4457
  });
@@ -4339,8 +4459,32 @@ var bundle = (input) => Effect24.gen(function* () {
4339
4459
  if (!output) {
4340
4460
  throw new Error("esbuild produced no output");
4341
4461
  }
4342
- return output.text;
4462
+ const bundleResult = { code: output.text };
4463
+ if (result.metafile) {
4464
+ bundleResult.topModules = analyzeMetafile(result.metafile);
4465
+ }
4466
+ return bundleResult;
4343
4467
  });
4468
+ var analyzeMetafile = (metafile) => {
4469
+ const packageSizes = /* @__PURE__ */ new Map();
4470
+ for (const [filePath, info] of Object.entries(metafile.inputs)) {
4471
+ const nodeModIdx = filePath.lastIndexOf("node_modules/");
4472
+ let key;
4473
+ if (nodeModIdx !== -1) {
4474
+ const afterNm = filePath.slice(nodeModIdx + "node_modules/".length);
4475
+ if (afterNm.startsWith("@")) {
4476
+ const parts = afterNm.split("/");
4477
+ key = `${parts[0]}/${parts[1]}`;
4478
+ } else {
4479
+ key = afterNm.split("/")[0];
4480
+ }
4481
+ } else {
4482
+ key = "<project>";
4483
+ }
4484
+ packageSizes.set(key, (packageSizes.get(key) ?? 0) + info.bytes);
4485
+ }
4486
+ return Array.from(packageSizes.entries()).map(([p, bytes]) => ({ path: p, bytes })).sort((a, b) => b.bytes - a.bytes);
4487
+ };
4344
4488
  var bundleMiddleware = (input) => Effect24.gen(function* () {
4345
4489
  const absFile = path3.isAbsolute(input.file) ? input.file : path3.resolve(input.projectDir, input.file);
4346
4490
  const source = fsSync2.readFileSync(absFile, "utf-8");
@@ -4371,6 +4515,106 @@ var bundleMiddleware = (input) => Effect24.gen(function* () {
4371
4515
  }
4372
4516
  return output.text;
4373
4517
  });
4518
+ var bundleAuthMiddleware = (input) => Effect24.gen(function* () {
4519
+ const { authConfig, secret } = input;
4520
+ const loginPath = authConfig.loginPath;
4521
+ const publicPatterns = authConfig.public ?? [];
4522
+ const entryPoint = `
4523
+ import { createHmac } from "crypto";
4524
+
4525
+ const SECRET = ${JSON.stringify(secret)};
4526
+ const LOGIN_PATH = ${JSON.stringify(loginPath)};
4527
+ const PUBLIC = ${JSON.stringify(publicPatterns)};
4528
+ const COOKIE = ${JSON.stringify(AUTH_COOKIE_NAME)};
4529
+
4530
+ const isPublic = (uri) => {
4531
+ for (const p of PUBLIC) {
4532
+ if (p.endsWith("/*")) {
4533
+ if (uri.startsWith(p.slice(0, -1))) return true;
4534
+ } else if (p.endsWith("*")) {
4535
+ if (uri.startsWith(p.slice(0, -1))) return true;
4536
+ } else {
4537
+ if (uri === p) return true;
4538
+ }
4539
+ }
4540
+ return false;
4541
+ };
4542
+
4543
+ const verify = (cookie) => {
4544
+ if (!cookie) return false;
4545
+ const dot = cookie.indexOf(".");
4546
+ if (dot === -1) return false;
4547
+ const payload = cookie.slice(0, dot);
4548
+ const sig = cookie.slice(dot + 1);
4549
+ const expected = createHmac("sha256", SECRET).update(payload).digest("base64url");
4550
+ if (sig !== expected) return false;
4551
+ try {
4552
+ const data = JSON.parse(Buffer.from(payload, "base64url").toString("utf-8"));
4553
+ return data.exp > Math.floor(Date.now() / 1000);
4554
+ } catch { return false; }
4555
+ };
4556
+
4557
+ const parseCookies = (headers) => {
4558
+ const cookies = {};
4559
+ const cookieHeaders = headers.cookie;
4560
+ if (!cookieHeaders) return cookies;
4561
+ for (const { value } of cookieHeaders) {
4562
+ for (const pair of value.split(";")) {
4563
+ const eq = pair.indexOf("=");
4564
+ if (eq === -1) continue;
4565
+ const name = pair.slice(0, eq).trim();
4566
+ const val = pair.slice(eq + 1).trim();
4567
+ if (name) cookies[name] = val;
4568
+ }
4569
+ }
4570
+ return cookies;
4571
+ };
4572
+
4573
+ const rewrite = (uri) => {
4574
+ if (uri.endsWith("/")) return uri + "index.html";
4575
+ if (!uri.includes(".")) return uri + "/index.html";
4576
+ return uri;
4577
+ };
4578
+
4579
+ export const handler = async (event) => {
4580
+ const req = event.Records[0].cf.request;
4581
+
4582
+ if (isPublic(req.uri)) {
4583
+ req.uri = rewrite(req.uri);
4584
+ return req;
4585
+ }
4586
+
4587
+ const cookies = parseCookies(req.headers);
4588
+ if (verify(cookies[COOKIE])) {
4589
+ req.uri = rewrite(req.uri);
4590
+ return req;
4591
+ }
4592
+
4593
+ return {
4594
+ status: "302",
4595
+ statusDescription: "Found",
4596
+ headers: { location: [{ key: "Location", value: LOGIN_PATH }] },
4597
+ };
4598
+ };
4599
+ `;
4600
+ const result = yield* Effect24.tryPromise({
4601
+ try: () => esbuild.build({
4602
+ stdin: { contents: entryPoint, loader: "js", resolveDir: process.cwd() },
4603
+ bundle: true,
4604
+ platform: "node",
4605
+ target: "node22",
4606
+ write: false,
4607
+ minify: true,
4608
+ sourcemap: false,
4609
+ format: "esm",
4610
+ external: ["crypto"]
4611
+ }),
4612
+ catch: (error) => new Error(`esbuild failed (auth middleware): ${error}`)
4613
+ });
4614
+ const output = result.outputFiles?.[0];
4615
+ if (!output) throw new Error("esbuild produced no output for auth middleware");
4616
+ return output.text;
4617
+ });
4374
4618
  var FIXED_DATE2 = /* @__PURE__ */ new Date(0);
4375
4619
  var zip = (input) => Effect24.async((resume) => {
4376
4620
  const chunks = [];
@@ -4470,21 +4714,25 @@ var flattenHandlers = (discovered) => {
4470
4714
  ];
4471
4715
  };
4472
4716
 
4717
+ // src/deploy/deploy.ts
4718
+ import * as crypto5 from "crypto";
4719
+
4473
4720
  // src/deploy/resolve-config.ts
4474
4721
  import { Effect as Effect25 } from "effect";
4475
- var collectRequiredParams = (handlers, project, stage) => {
4722
+ var collectRequiredSecrets = (handlers, project, stage) => {
4476
4723
  const seen = /* @__PURE__ */ new Map();
4477
4724
  const collect = (handlerGroups) => {
4478
4725
  for (const { exports } of handlerGroups) {
4479
4726
  for (const fn13 of exports) {
4480
- for (const { propName, ssmKey } of fn13.paramEntries) {
4727
+ for (const { propName, ssmKey, generate } of fn13.secretEntries) {
4481
4728
  const ssmPath = `/${project}/${stage}/${ssmKey}`;
4482
4729
  if (!seen.has(ssmPath)) {
4483
4730
  seen.set(ssmPath, {
4484
4731
  ssmPath,
4485
4732
  propName,
4486
4733
  ssmKey,
4487
- handlerName: fn13.exportName
4734
+ handlerName: fn13.exportName,
4735
+ ...generate ? { generate } : {}
4488
4736
  });
4489
4737
  }
4490
4738
  }
@@ -4497,11 +4745,34 @@ var collectRequiredParams = (handlers, project, stage) => {
4497
4745
  collect(handlers.apiHandlers);
4498
4746
  return Array.from(seen.values());
4499
4747
  };
4500
- var checkMissingParams = (params) => Effect25.gen(function* () {
4501
- if (params.length === 0) return { existing: [], missing: [] };
4748
+ var AUTH_SSM_KEY = "_auth-secret";
4749
+ var collectAuthSecret = (handlers, project, stage) => {
4750
+ const hasAuth = (groups) => groups.some((g) => g.exports.some((fn13) => fn13.authConfig));
4751
+ if (hasAuth(handlers.apiHandlers) || hasAuth(handlers.staticSiteHandlers)) {
4752
+ return {
4753
+ ssmPath: `/${project}/${stage}/${AUTH_SSM_KEY}`,
4754
+ propName: "_authSecret",
4755
+ ssmKey: AUTH_SSM_KEY,
4756
+ handlerName: "_auth",
4757
+ generate: { type: "hex", bytes: 32 }
4758
+ };
4759
+ }
4760
+ return void 0;
4761
+ };
4762
+ var fetchAuthSecretValue = (ssmPath) => Effect25.gen(function* () {
4763
+ const result = yield* ssm_exports.make("get_parameters", {
4764
+ Names: [ssmPath],
4765
+ WithDecryption: true
4766
+ });
4767
+ const value = result.Parameters?.[0]?.Value;
4768
+ if (!value) throw new Error(`Auth secret not found at ${ssmPath}`);
4769
+ return value;
4770
+ });
4771
+ var checkMissingSecrets = (secrets) => Effect25.gen(function* () {
4772
+ if (secrets.length === 0) return { existing: [], missing: [] };
4502
4773
  const existingNames = /* @__PURE__ */ new Set();
4503
- for (let i = 0; i < params.length; i += 10) {
4504
- const batch = params.slice(i, i + 10);
4774
+ for (let i = 0; i < secrets.length; i += 10) {
4775
+ const batch = secrets.slice(i, i + 10);
4505
4776
  const result = yield* ssm_exports.make("get_parameters", {
4506
4777
  Names: batch.map((p) => p.ssmPath),
4507
4778
  WithDecryption: false
@@ -4512,7 +4783,7 @@ var checkMissingParams = (params) => Effect25.gen(function* () {
4512
4783
  }
4513
4784
  const existing = [];
4514
4785
  const missing = [];
4515
- for (const p of params) {
4786
+ for (const p of secrets) {
4516
4787
  if (existingNames.has(p.ssmPath)) {
4517
4788
  existing.push(p);
4518
4789
  } else {
@@ -4530,6 +4801,12 @@ import { toSeconds } from "effortless-aws";
4530
4801
  import { Effect as Effect26 } from "effect";
4531
4802
  import * as fs3 from "fs/promises";
4532
4803
  import * as path4 from "path";
4804
+ var formatBytes = (bytes) => {
4805
+ if (bytes < 1024) return `${bytes}B`;
4806
+ const kb = bytes / 1024;
4807
+ if (kb < 1024) return `${kb.toFixed(1)}KB`;
4808
+ return `${(kb / 1024).toFixed(2)}MB`;
4809
+ };
4533
4810
  var readSource = (input) => Effect26.gen(function* () {
4534
4811
  if ("code" in input && typeof input.code === "string") {
4535
4812
  return input.code;
@@ -4542,10 +4819,11 @@ var ensureLayerAndExternal = (input) => Effect26.gen(function* () {
4542
4819
  project: input.project,
4543
4820
  stage: input.stage,
4544
4821
  region: input.region,
4545
- projectDir: input.packageDir
4822
+ projectDir: input.packageDir,
4823
+ extraNodeModules: input.extraNodeModules
4546
4824
  });
4547
4825
  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: [] };
4826
+ const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect26.sync(() => collectLayerPackages(input.packageDir, prodDeps, input.extraNodeModules)) : { packages: [], warnings: [] };
4549
4827
  for (const warning of layerWarnings) {
4550
4828
  yield* Effect26.logWarning(`[layer] ${warning}`);
4551
4829
  }
@@ -4590,14 +4868,21 @@ var deployCoreLambda = ({
4590
4868
  mergedPermissions.length > 0 ? mergedPermissions : void 0,
4591
4869
  makeTags(tagCtx, "iam-role")
4592
4870
  );
4593
- const bundled = yield* bundle({
4871
+ const bundleResult = yield* bundle({
4594
4872
  ...input,
4595
4873
  exportName,
4596
4874
  ...bundleType ? { type: bundleType } : {},
4597
4875
  ...external && external.length > 0 ? { external } : {}
4598
4876
  });
4599
4877
  const staticFiles = staticGlobs && staticGlobs.length > 0 ? resolveStaticFiles(staticGlobs, input.projectDir) : void 0;
4600
- const code = yield* zip({ content: bundled, staticFiles });
4878
+ const bundleSize = Buffer.byteLength(bundleResult.code, "utf-8");
4879
+ if (bundleResult.topModules && bundleSize > 500 * 1024) {
4880
+ const top = bundleResult.topModules.slice(0, 10);
4881
+ const lines = top.map((m) => ` ${formatBytes(m.bytes).padStart(8)} ${m.path}`).join("\n");
4882
+ yield* Effect26.logWarning(`Bundle "${handlerName}" is ${formatBytes(bundleSize)} \u2014 top modules:
4883
+ ${lines}`);
4884
+ }
4885
+ const code = yield* zip({ content: bundleResult.code, staticFiles });
4601
4886
  const environment = {
4602
4887
  EFF_PROJECT: input.project,
4603
4888
  EFF_STAGE: tagCtx.stage,
@@ -4617,7 +4902,7 @@ var deployCoreLambda = ({
4617
4902
  ...layerArn ? { layers: [layerArn] } : {},
4618
4903
  environment
4619
4904
  });
4620
- return { functionArn, status, tagCtx };
4905
+ return { functionArn, status, tagCtx, bundleSize };
4621
4906
  });
4622
4907
 
4623
4908
  // src/deploy/deploy-table.ts
@@ -4639,7 +4924,7 @@ var deployTableFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsP
4639
4924
  tags: makeTags(tagCtx, "dynamodb")
4640
4925
  });
4641
4926
  const selfEnv = { EFF_DEP_SELF: `table:${tableName}`, ...depsEnv };
4642
- const { functionArn, status } = yield* deployCoreLambda({
4927
+ const { functionArn, status, bundleSize } = yield* deployCoreLambda({
4643
4928
  input,
4644
4929
  exportName,
4645
4930
  handlerName,
@@ -4667,6 +4952,7 @@ var deployTableFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsP
4667
4952
  exportName,
4668
4953
  functionArn,
4669
4954
  status,
4955
+ bundleSize,
4670
4956
  tableArn,
4671
4957
  streamArn
4672
4958
  };
@@ -4683,7 +4969,8 @@ var deployTable = (input) => Effect27.gen(function* () {
4683
4969
  project: input.project,
4684
4970
  stage: resolveStage(input.stage),
4685
4971
  region: input.region,
4686
- packageDir: input.packageDir ?? input.projectDir
4972
+ packageDir: input.packageDir ?? input.projectDir,
4973
+ extraNodeModules: input.extraNodeModules
4687
4974
  });
4688
4975
  const result = yield* deployTableFunction({
4689
4976
  input,
@@ -4712,7 +4999,8 @@ var deployAllTables = (input) => Effect27.gen(function* () {
4712
4999
  project: input.project,
4713
5000
  stage: resolveStage(input.stage),
4714
5001
  region: input.region,
4715
- packageDir: input.packageDir ?? input.projectDir
5002
+ packageDir: input.packageDir ?? input.projectDir,
5003
+ extraNodeModules: input.extraNodeModules
4716
5004
  });
4717
5005
  const results = yield* Effect27.forEach(
4718
5006
  functions,
@@ -4741,7 +5029,7 @@ import { toSeconds as toSeconds2 } from "effortless-aws";
4741
5029
  var deployApiFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect28.gen(function* () {
4742
5030
  const { exportName, config } = fn13;
4743
5031
  const handlerName = exportName;
4744
- const { functionArn, status } = yield* deployCoreLambda({
5032
+ const { functionArn, status, bundleSize } = yield* deployCoreLambda({
4745
5033
  input,
4746
5034
  exportName,
4747
5035
  handlerName,
@@ -4755,7 +5043,7 @@ var deployApiFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPer
4755
5043
  ...depsPermissions ? { depsPermissions } : {},
4756
5044
  ...staticGlobs && staticGlobs.length > 0 ? { staticGlobs } : {}
4757
5045
  });
4758
- return { exportName, functionArn, status, config, handlerName };
5046
+ return { exportName, functionArn, status, bundleSize, config, handlerName };
4759
5047
  });
4760
5048
  var deploy = (input) => Effect28.gen(function* () {
4761
5049
  const source = yield* readSource(input);
@@ -4776,7 +5064,8 @@ var deploy = (input) => Effect28.gen(function* () {
4776
5064
  project: input.project,
4777
5065
  stage: tagCtx.stage,
4778
5066
  region: input.region,
4779
- packageDir: input.packageDir ?? input.projectDir
5067
+ packageDir: input.packageDir ?? input.projectDir,
5068
+ extraNodeModules: input.extraNodeModules
4780
5069
  });
4781
5070
  const { functionArn } = yield* deployApiFunction({
4782
5071
  input,
@@ -5138,6 +5427,12 @@ var deployMiddlewareLambda = (input) => Effect31.gen(function* () {
5138
5427
  makeTags(tagCtx, "iam-role")
5139
5428
  );
5140
5429
  const bundled = yield* bundleMiddleware({ projectDir, file });
5430
+ const bundleSizeKB = Math.round(bundled.length / 1024);
5431
+ if (bundleSizeKB > 50) {
5432
+ yield* Effect31.logWarning(
5433
+ `[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").`
5434
+ );
5435
+ }
5141
5436
  const code = yield* zip({ content: bundled });
5142
5437
  const { functionArn } = yield* ensureLambda({
5143
5438
  project,
@@ -5153,14 +5448,52 @@ var deployMiddlewareLambda = (input) => Effect31.gen(function* () {
5153
5448
  }).pipe(
5154
5449
  Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5155
5450
  );
5156
- const { versionArn } = yield* publishVersion(
5157
- `${project}-${stage}-${middlewareName}`
5158
- ).pipe(
5451
+ const edgeFunctionName = `${project}-${stage}-${middlewareName}`;
5452
+ yield* ensureEdgePermission(edgeFunctionName).pipe(
5453
+ Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5454
+ );
5455
+ const { versionArn } = yield* publishVersion(edgeFunctionName).pipe(
5159
5456
  Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5160
5457
  );
5161
5458
  yield* Effect31.logDebug(`Middleware deployed: ${versionArn}`);
5162
5459
  return { versionArn };
5163
5460
  });
5461
+ var deployAuthMiddlewareLambda = (input) => Effect31.gen(function* () {
5462
+ const { project, stage, handlerName, tagCtx, authConfig, authSecret } = input;
5463
+ const middlewareName = `${handlerName}-middleware`;
5464
+ yield* Effect31.logDebug(`Deploying auth middleware Lambda@Edge: ${middlewareName}`);
5465
+ const roleArn = yield* ensureEdgeRole(
5466
+ project,
5467
+ stage,
5468
+ middlewareName,
5469
+ makeTags(tagCtx, "iam-role")
5470
+ );
5471
+ const bundled = yield* bundleAuthMiddleware({ authConfig, secret: authSecret });
5472
+ const code = yield* zip({ content: bundled });
5473
+ const edgeFunctionName = `${project}-${stage}-${middlewareName}`;
5474
+ yield* ensureLambda({
5475
+ project,
5476
+ stage,
5477
+ name: middlewareName,
5478
+ region: "us-east-1",
5479
+ roleArn,
5480
+ code,
5481
+ memory: 128,
5482
+ timeout: 5,
5483
+ architecture: Architecture3.x86_64,
5484
+ tags: makeTags(tagCtx, "lambda")
5485
+ }).pipe(
5486
+ Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5487
+ );
5488
+ yield* ensureEdgePermission(edgeFunctionName).pipe(
5489
+ Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5490
+ );
5491
+ const { versionArn } = yield* publishVersion(edgeFunctionName).pipe(
5492
+ Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
5493
+ );
5494
+ yield* Effect31.logDebug(`Auth middleware deployed: ${versionArn}`);
5495
+ return { versionArn };
5496
+ });
5164
5497
  var ERROR_PAGE_KEY = "_effortless/404.html";
5165
5498
  var generateErrorPageHtml = () => `<!DOCTYPE html>
5166
5499
  <html lang="en">
@@ -5265,7 +5598,20 @@ var deployStaticSite = (input) => Effect31.gen(function* () {
5265
5598
  const isSpa = config.spa ?? false;
5266
5599
  let urlRewriteFunctionArn;
5267
5600
  let lambdaEdgeArn;
5268
- if (hasMiddleware && input.file) {
5601
+ const hasAuth = !!(input.authSecret && input.authConfig);
5602
+ if (hasAuth) {
5603
+ const authResult = yield* deployAuthMiddlewareLambda({
5604
+ project,
5605
+ stage,
5606
+ handlerName,
5607
+ tagCtx,
5608
+ authConfig: input.authConfig,
5609
+ authSecret: input.authSecret
5610
+ }).pipe(
5611
+ Effect31.provide(clients_exports.makeClients({ iam: { region: "us-east-1" } }))
5612
+ );
5613
+ lambdaEdgeArn = authResult.versionArn;
5614
+ } else if (hasMiddleware && input.file) {
5269
5615
  const result = yield* deployMiddlewareLambda({
5270
5616
  projectDir,
5271
5617
  project,
@@ -5395,7 +5741,7 @@ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, d
5395
5741
  EFF_QUEUE_ARN: queueArn,
5396
5742
  ...depsEnv
5397
5743
  };
5398
- const { functionArn, status } = yield* deployCoreLambda({
5744
+ const { functionArn, status, bundleSize } = yield* deployCoreLambda({
5399
5745
  input,
5400
5746
  exportName,
5401
5747
  handlerName,
@@ -5422,6 +5768,7 @@ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, d
5422
5768
  exportName,
5423
5769
  functionArn,
5424
5770
  status,
5771
+ bundleSize,
5425
5772
  queueUrl,
5426
5773
  queueArn
5427
5774
  };
@@ -5456,7 +5803,7 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
5456
5803
  };
5457
5804
  }
5458
5805
  const selfEnv = { EFF_DEP_SELF: `bucket:${bucketName}`, ...depsEnv };
5459
- const { functionArn, status } = yield* deployCoreLambda({
5806
+ const { functionArn, status, bundleSize } = yield* deployCoreLambda({
5460
5807
  input,
5461
5808
  exportName,
5462
5809
  handlerName,
@@ -5484,6 +5831,7 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
5484
5831
  exportName,
5485
5832
  functionArn,
5486
5833
  status,
5834
+ bundleSize,
5487
5835
  bucketName,
5488
5836
  bucketArn
5489
5837
  };
@@ -5564,10 +5912,17 @@ var createLiveProgress = (manifest) => {
5564
5912
  const sec = ((Date.now() - startTime) / 1e3).toFixed(1);
5565
5913
  return c.dim(`${sec}s`);
5566
5914
  };
5567
- return (name, type, status) => Effect35.sync(() => {
5915
+ const formatSize = (bytes) => {
5916
+ if (bytes < 1024) return `${bytes}B`;
5917
+ const kb = bytes / 1024;
5918
+ if (kb < 1024) return `${kb.toFixed(0)}KB`;
5919
+ return `${(kb / 1024).toFixed(1)}MB`;
5920
+ };
5921
+ return (name, type, status, bundleSize) => Effect35.sync(() => {
5568
5922
  const key = `${name}:${type}`;
5569
5923
  results.set(key, status);
5570
- const line = ` ${name} ${c.dim(`(${type})`)} ${statusLabel(status)} ${formatDuration()}`;
5924
+ const sizeInfo = bundleSize ? ` ${c.dim(formatSize(bundleSize))}` : "";
5925
+ const line = ` ${name} ${c.dim(`(${type})`)} ${statusLabel(status)}${sizeInfo} ${formatDuration()}`;
5571
5926
  if (isTTY) {
5572
5927
  const idx = lineIndex.get(key) ?? 0;
5573
5928
  const up = manifest.length - idx;
@@ -5576,7 +5931,7 @@ var createLiveProgress = (manifest) => {
5576
5931
  clearInterval(timer);
5577
5932
  }
5578
5933
  } else {
5579
- process.stdout.write(` ${c.dim(`[${results.size}/${manifest.length}]`)} ${name} ${c.dim(`(${type})`)} ${statusLabel(status)} ${formatDuration()}
5934
+ process.stdout.write(` ${c.dim(`[${results.size}/${manifest.length}]`)} ${name} ${c.dim(`(${type})`)} ${statusLabel(status)}${sizeInfo} ${formatDuration()}
5580
5935
  `);
5581
5936
  }
5582
5937
  });
@@ -5587,7 +5942,8 @@ var prepareLayer = (input) => Effect35.gen(function* () {
5587
5942
  project: input.project,
5588
5943
  stage: input.stage,
5589
5944
  region: input.region,
5590
- projectDir: input.packageDir
5945
+ projectDir: input.packageDir,
5946
+ extraNodeModules: input.extraNodeModules
5591
5947
  }).pipe(
5592
5948
  Effect35.provide(
5593
5949
  clients_exports.makeClients({
@@ -5596,7 +5952,7 @@ var prepareLayer = (input) => Effect35.gen(function* () {
5596
5952
  )
5597
5953
  );
5598
5954
  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: [] };
5955
+ const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect35.sync(() => collectLayerPackages(input.packageDir, prodDeps, input.extraNodeModules)) : { packages: [], warnings: [] };
5600
5956
  for (const warning of layerWarnings) {
5601
5957
  yield* Effect35.logWarning(`[layer] ${warning}`);
5602
5958
  }
@@ -5742,10 +6098,20 @@ var SSM_PERMISSIONS = [
5742
6098
  "ssm:GetParameter",
5743
6099
  "ssm:GetParameters"
5744
6100
  ];
5745
- var resolveParams = (paramEntries, project, stage) => {
5746
- if (paramEntries.length === 0) return void 0;
6101
+ var executeGenerateSpec = (spec) => {
6102
+ switch (spec.type) {
6103
+ case "hex":
6104
+ return crypto5.randomBytes(spec.bytes).toString("hex");
6105
+ case "base64":
6106
+ return crypto5.randomBytes(spec.bytes).toString("base64url");
6107
+ case "uuid":
6108
+ return crypto5.randomUUID();
6109
+ }
6110
+ };
6111
+ var resolveSecrets = (secretEntries, project, stage) => {
6112
+ if (secretEntries.length === 0) return void 0;
5747
6113
  const paramsEnv = {};
5748
- for (const { propName, ssmKey } of paramEntries) {
6114
+ for (const { propName, ssmKey } of secretEntries) {
5749
6115
  paramsEnv[`EFF_PARAM_${propName}`] = `/${project}/${stage}/${ssmKey}`;
5750
6116
  }
5751
6117
  return { paramsEnv, paramsPermissions: SSM_PERMISSIONS };
@@ -5764,10 +6130,10 @@ var makeDeployInput = (ctx, file) => ({
5764
6130
  region: ctx.input.region,
5765
6131
  ...ctx.input.stage ? { stage: ctx.input.stage } : {}
5766
6132
  });
5767
- var resolveHandlerEnv = (depsKeys, paramEntries, ctx) => {
6133
+ var resolveHandlerEnv = (depsKeys, secretEntries, ctx) => {
5768
6134
  const resolved = mergeResolved(
5769
6135
  resolveDeps(depsKeys, ctx.tableNameMap, ctx.bucketNameMap, ctx.mailerDomainMap, ctx.queueNameMap),
5770
- resolveParams(paramEntries, ctx.input.project, ctx.stage)
6136
+ resolveSecrets(secretEntries, ctx.input.project, ctx.stage)
5771
6137
  );
5772
6138
  return {
5773
6139
  depsEnv: resolved?.depsEnv ?? {},
@@ -5781,7 +6147,7 @@ var buildTableTasks = (ctx, handlers, results) => {
5781
6147
  for (const fn13 of exports) {
5782
6148
  tasks.push(
5783
6149
  Effect35.gen(function* () {
5784
- const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
6150
+ const env = resolveHandlerEnv(fn13.depsKeys, fn13.secretEntries, ctx);
5785
6151
  const result = yield* deployTableFunction({
5786
6152
  input: makeDeployInput(ctx, file),
5787
6153
  fn: fn13,
@@ -5792,7 +6158,7 @@ var buildTableTasks = (ctx, handlers, results) => {
5792
6158
  ...fn13.staticGlobs.length > 0 ? { staticGlobs: fn13.staticGlobs } : {}
5793
6159
  }).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, dynamodb: { region } })));
5794
6160
  results.push(result);
5795
- yield* ctx.logComplete(fn13.exportName, "table", result.status);
6161
+ yield* ctx.logComplete(fn13.exportName, "table", result.status, result.bundleSize);
5796
6162
  })
5797
6163
  );
5798
6164
  }
@@ -5837,6 +6203,12 @@ var buildStaticSiteTasks = (ctx, handlers, results, apiOriginDomain) => {
5837
6203
  for (const fn13 of exports) {
5838
6204
  tasks.push(
5839
6205
  Effect35.gen(function* () {
6206
+ let authSecretValue;
6207
+ if (fn13.authConfig && ctx.authSecretPath) {
6208
+ authSecretValue = yield* fetchAuthSecretValue(ctx.authSecretPath).pipe(
6209
+ Effect35.provide(clients_exports.makeClients({ ssm: { region: ctx.input.region } }))
6210
+ );
6211
+ }
5840
6212
  const result = yield* deployStaticSite({
5841
6213
  projectDir: ctx.input.projectDir,
5842
6214
  project: ctx.input.project,
@@ -5845,7 +6217,8 @@ var buildStaticSiteTasks = (ctx, handlers, results, apiOriginDomain) => {
5845
6217
  fn: fn13,
5846
6218
  verbose: ctx.input.verbose,
5847
6219
  ...fn13.hasHandler ? { file } : {},
5848
- ...apiOriginDomain ? { apiOriginDomain } : {}
6220
+ ...apiOriginDomain ? { apiOriginDomain } : {},
6221
+ ...authSecretValue && fn13.authConfig ? { authSecret: authSecretValue, authConfig: fn13.authConfig } : {}
5849
6222
  }).pipe(Effect35.provide(clients_exports.makeClients({
5850
6223
  s3: { region },
5851
6224
  cloudfront: { region: "us-east-1" },
@@ -5867,7 +6240,7 @@ var buildFifoQueueTasks = (ctx, handlers, results) => {
5867
6240
  for (const fn13 of exports) {
5868
6241
  tasks.push(
5869
6242
  Effect35.gen(function* () {
5870
- const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
6243
+ const env = resolveHandlerEnv(fn13.depsKeys, fn13.secretEntries, ctx);
5871
6244
  const result = yield* deployFifoQueueFunction({
5872
6245
  input: makeDeployInput(ctx, file),
5873
6246
  fn: fn13,
@@ -5878,7 +6251,7 @@ var buildFifoQueueTasks = (ctx, handlers, results) => {
5878
6251
  ...fn13.staticGlobs.length > 0 ? { staticGlobs: fn13.staticGlobs } : {}
5879
6252
  }).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, sqs: { region } })));
5880
6253
  results.push(result);
5881
- yield* ctx.logComplete(fn13.exportName, "queue", result.status);
6254
+ yield* ctx.logComplete(fn13.exportName, "queue", result.status, result.bundleSize);
5882
6255
  })
5883
6256
  );
5884
6257
  }
@@ -5892,7 +6265,7 @@ var buildBucketTasks = (ctx, handlers, results) => {
5892
6265
  for (const fn13 of exports) {
5893
6266
  tasks.push(
5894
6267
  Effect35.gen(function* () {
5895
- const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
6268
+ const env = resolveHandlerEnv(fn13.depsKeys, fn13.secretEntries, ctx);
5896
6269
  const result = yield* deployBucketFunction({
5897
6270
  input: makeDeployInput(ctx, file),
5898
6271
  fn: fn13,
@@ -5904,7 +6277,7 @@ var buildBucketTasks = (ctx, handlers, results) => {
5904
6277
  }).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, s3: { region } })));
5905
6278
  results.push(result);
5906
6279
  const status = result.status === "resource-only" ? "created" : result.status;
5907
- yield* ctx.logComplete(fn13.exportName, "bucket", status);
6280
+ yield* ctx.logComplete(fn13.exportName, "bucket", status, result.bundleSize);
5908
6281
  })
5909
6282
  );
5910
6283
  }
@@ -5939,8 +6312,12 @@ var buildApiTasks = (ctx, handlers, results) => {
5939
6312
  for (const fn13 of exports) {
5940
6313
  tasks.push(
5941
6314
  Effect35.gen(function* () {
5942
- const env = resolveHandlerEnv(fn13.depsKeys, fn13.paramEntries, ctx);
5943
- const { exportName, functionArn, status, handlerName } = yield* deployApiFunction({
6315
+ const env = resolveHandlerEnv(fn13.depsKeys, fn13.secretEntries, ctx);
6316
+ if (fn13.authConfig && ctx.authSecretPath) {
6317
+ env.depsEnv["EFF_AUTH_SECRET"] = ctx.authSecretPath;
6318
+ env.depsPermissions = [...env.depsPermissions, "ssm:GetParameter", "ssm:GetParameters"];
6319
+ }
6320
+ const { exportName, functionArn, status, bundleSize, handlerName } = yield* deployApiFunction({
5944
6321
  input: makeDeployInput(ctx, file),
5945
6322
  fn: fn13,
5946
6323
  ...ctx.layerArn ? { layerArn: ctx.layerArn } : {},
@@ -5957,7 +6334,7 @@ var buildApiTasks = (ctx, handlers, results) => {
5957
6334
  Effect35.provide(clients_exports.makeClients({ lambda: { region } }))
5958
6335
  );
5959
6336
  results.push({ exportName, url: functionUrl, functionArn });
5960
- yield* ctx.logComplete(exportName, "api", status);
6337
+ yield* ctx.logComplete(exportName, "api", status, bundleSize);
5961
6338
  })
5962
6339
  );
5963
6340
  }
@@ -5994,16 +6371,33 @@ var deployProject = (input) => Effect35.gen(function* () {
5994
6371
  yield* Console2.log(`
5995
6372
  ${c.dim("Handlers:")} ${parts.join(", ")}`);
5996
6373
  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(
6374
+ const requiredSecrets = collectRequiredSecrets(discovered, input.project, stage);
6375
+ const authSecret = collectAuthSecret(discovered, input.project, stage);
6376
+ if (authSecret) requiredSecrets.push(authSecret);
6377
+ if (requiredSecrets.length > 0) {
6378
+ const { missing } = yield* checkMissingSecrets(requiredSecrets).pipe(
6000
6379
  Effect35.provide(clients_exports.makeClients({ ssm: { region: input.region } }))
6001
6380
  );
6002
- if (missing.length > 0) {
6381
+ const withGenerators = missing.filter((m) => m.generate);
6382
+ const manualOnly = missing.filter((m) => !m.generate);
6383
+ if (withGenerators.length > 0) {
6384
+ for (const entry of withGenerators) {
6385
+ const value = executeGenerateSpec(entry.generate);
6386
+ yield* ssm_exports.make("put_parameter", {
6387
+ Name: entry.ssmPath,
6388
+ Value: value,
6389
+ Type: "SecureString"
6390
+ }).pipe(Effect35.provide(clients_exports.makeClients({ ssm: { region: input.region } })));
6391
+ yield* Effect35.logDebug(`Auto-created SSM parameter: ${entry.ssmPath}`);
6392
+ }
6003
6393
  yield* Console2.log(`
6004
- ${c.yellow("\u26A0")} Missing ${missing.length} SSM parameter(s):
6394
+ ${c.green("\u2713")} Auto-created ${withGenerators.length} secret(s)`);
6395
+ }
6396
+ if (manualOnly.length > 0) {
6397
+ yield* Console2.log(`
6398
+ ${c.yellow("\u26A0")} Missing ${manualOnly.length} SSM parameter(s):
6005
6399
  `);
6006
- for (const p of missing) {
6400
+ for (const p of manualOnly) {
6007
6401
  yield* Console2.log(` ${c.dim(p.handlerName)} \u2192 ${c.yellow(p.ssmPath)}`);
6008
6402
  }
6009
6403
  yield* Console2.log(`
@@ -6027,7 +6421,8 @@ var deployProject = (input) => Effect35.gen(function* () {
6027
6421
  project: input.project,
6028
6422
  stage,
6029
6423
  region: input.region,
6030
- packageDir: input.packageDir ?? input.projectDir
6424
+ packageDir: input.packageDir ?? input.projectDir,
6425
+ extraNodeModules: input.extraNodeModules
6031
6426
  }) : { layerArn: void 0, layerVersion: void 0, layerStatus: void 0, external: [] };
6032
6427
  if (layerArn && layerStatus) {
6033
6428
  const status = layerStatus === "cached" ? c.dim("cached") : c.green("created");
@@ -6062,7 +6457,8 @@ var deployProject = (input) => Effect35.gen(function* () {
6062
6457
  bucketNameMap,
6063
6458
  mailerDomainMap,
6064
6459
  queueNameMap,
6065
- logComplete
6460
+ logComplete,
6461
+ ...authSecret ? { authSecretPath: authSecret.ssmPath } : {}
6066
6462
  };
6067
6463
  const tableResults = [];
6068
6464
  const appResults = [];
@@ -6265,7 +6661,8 @@ var deployCommand = Command.make(
6265
6661
  stage: finalStage,
6266
6662
  region: finalRegion,
6267
6663
  noSites,
6268
- verbose
6664
+ verbose,
6665
+ extraNodeModules: projectDir !== cwd ? [path10.join(projectDir, "node_modules")] : void 0
6269
6666
  });
6270
6667
  const total = results.tableResults.length + results.appResults.length + results.staticSiteResults.length + results.apiResults.length;
6271
6668
  yield* Console3.log(`
@@ -6359,7 +6756,8 @@ Deployed ${tableResults.length} table handler(s):`));
6359
6756
  project,
6360
6757
  stage: finalStage,
6361
6758
  region: finalRegion,
6362
- exportName: foundExport
6759
+ exportName: foundExport,
6760
+ extraNodeModules: projectDir !== cwd ? [path10.join(projectDir, "node_modules")] : void 0
6363
6761
  };
6364
6762
  if (handlerType === "table") {
6365
6763
  const result = yield* deployTable(input);
@@ -6464,9 +6862,9 @@ var STATUS_COLORS = {
6464
6862
  var formatStatus = (status) => {
6465
6863
  return STATUS_COLORS[status](status.padEnd(10));
6466
6864
  };
6467
- var formatRoute = (method, path12) => {
6468
- if (method && path12) return `${method.padEnd(5)} ${path12}`;
6469
- if (path12) return path12;
6865
+ var formatRoute = (method, path13) => {
6866
+ if (method && path13) return `${method.padEnd(5)} ${path13}`;
6867
+ if (path13) return path13;
6470
6868
  return "";
6471
6869
  };
6472
6870
  var formatEntry = (entry) => {
@@ -7112,15 +7510,16 @@ var layerCommand = Command5.make(
7112
7510
  "layer",
7113
7511
  { build: buildOption, output: outputOption, verbose: verboseOption },
7114
7512
  ({ build: build3, output, verbose }) => Effect43.gen(function* () {
7115
- const { config, cwd } = yield* ProjectConfig;
7513
+ const { config, cwd, projectDir } = yield* ProjectConfig;
7514
+ const extraNodeModules = projectDir !== cwd ? [path11.join(projectDir, "node_modules")] : void 0;
7116
7515
  if (build3) {
7117
- yield* buildLayer(cwd, output, verbose);
7516
+ yield* buildLayer(cwd, output, verbose, extraNodeModules);
7118
7517
  } else {
7119
- yield* showLayerInfo(cwd, config?.name, verbose);
7518
+ yield* showLayerInfo(cwd, config?.name, verbose, extraNodeModules);
7120
7519
  }
7121
7520
  }).pipe(Effect43.provide(ProjectConfig.Live))
7122
7521
  ).pipe(Command5.withDescription("Inspect or locally build the shared Lambda dependency layer from package.json"));
7123
- var showLayerInfo = (projectDir, projectName, verbose) => Effect43.gen(function* () {
7522
+ var showLayerInfo = (projectDir, projectName, verbose, extraNodeModules) => Effect43.gen(function* () {
7124
7523
  yield* Console7.log(`
7125
7524
  ${c.bold("=== Layer Packages Preview ===")}
7126
7525
  `);
@@ -7143,7 +7542,7 @@ ${c.bold("=== Layer Packages Preview ===")}
7143
7542
  for (const dep of prodDeps) {
7144
7543
  yield* Console7.log(` ${dep}`);
7145
7544
  }
7146
- const hash = yield* computeLockfileHash(projectDir).pipe(
7545
+ const hash = yield* computeLockfileHash(projectDir, extraNodeModules).pipe(
7147
7546
  Effect43.catchAll(() => Effect43.succeed(null))
7148
7547
  );
7149
7548
  if (hash) {
@@ -7152,7 +7551,7 @@ Lockfile hash: ${hash}`);
7152
7551
  } else {
7153
7552
  yield* Console7.log("\nNo lockfile found (package-lock.json, pnpm-lock.yaml, or yarn.lock)");
7154
7553
  }
7155
- const { packages: allPackages, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps));
7554
+ const { packages: allPackages, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps, extraNodeModules));
7156
7555
  if (layerWarnings.length > 0) {
7157
7556
  yield* Console7.log(c.yellow(`
7158
7557
  Warnings (${layerWarnings.length}):`));
@@ -7181,7 +7580,7 @@ Total packages for layer ${c.dim(`(${allPackages.length})`)}:`);
7181
7580
  Layer name: ${projectName}-deps`);
7182
7581
  }
7183
7582
  });
7184
- var buildLayer = (projectDir, output, verbose) => Effect43.gen(function* () {
7583
+ var buildLayer = (projectDir, output, verbose, extraNodeModules) => Effect43.gen(function* () {
7185
7584
  const outputDir = path11.isAbsolute(output) ? output : path11.resolve(projectDir, output);
7186
7585
  const layerDir = path11.join(outputDir, "nodejs", "node_modules");
7187
7586
  const layerRoot = path11.join(outputDir, "nodejs");
@@ -7211,12 +7610,12 @@ ${c.bold("=== Building Layer Locally ===")}
7211
7610
  for (const dep of prodDeps) {
7212
7611
  yield* Console7.log(` ${dep}`);
7213
7612
  }
7214
- const hash = yield* computeLockfileHash(projectDir).pipe(
7613
+ const hash = yield* computeLockfileHash(projectDir, extraNodeModules).pipe(
7215
7614
  Effect43.catchAll(() => Effect43.succeed("unknown"))
7216
7615
  );
7217
7616
  yield* Console7.log(`
7218
7617
  Lockfile hash: ${hash}`);
7219
- const { packages: allPackages, resolvedPaths, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps));
7618
+ const { packages: allPackages, resolvedPaths, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps, extraNodeModules));
7220
7619
  if (layerWarnings.length > 0) {
7221
7620
  yield* Console7.log(`
7222
7621
  Warnings (${layerWarnings.length}):`);
@@ -7283,7 +7682,7 @@ var loadRequiredParams = (projectOpt, stage, region) => Effect44.gen(function* (
7283
7682
  const handlers = discoverHandlers(files);
7284
7683
  const finalStage = config?.stage ?? stage;
7285
7684
  const finalRegion = config?.region ?? region;
7286
- const params = collectRequiredParams(handlers, project, finalStage);
7685
+ const params = collectRequiredSecrets(handlers, project, finalStage);
7287
7686
  return { params, project, stage: finalStage, region: finalRegion };
7288
7687
  });
7289
7688
  var listCommand = Command6.make(
@@ -7296,7 +7695,7 @@ var listCommand = Command6.make(
7296
7695
  yield* Console8.log("No config parameters declared in handlers.");
7297
7696
  return;
7298
7697
  }
7299
- const { existing, missing } = yield* checkMissingParams(params).pipe(
7698
+ const { existing, missing } = yield* checkMissingSecrets(params).pipe(
7300
7699
  Effect44.provide(clients_exports.makeClients({ ssm: { region: ctx.region } }))
7301
7700
  );
7302
7701
  yield* Console8.log(`
@@ -7367,7 +7766,7 @@ var configRootCommand = Command6.make(
7367
7766
  yield* Console8.log("No config parameters declared in handlers.");
7368
7767
  return;
7369
7768
  }
7370
- const { missing } = yield* checkMissingParams(params).pipe(
7769
+ const { missing } = yield* checkMissingSecrets(params).pipe(
7371
7770
  Effect44.provide(clients_exports.makeClients({ ssm: { region: ctx.region } }))
7372
7771
  );
7373
7772
  if (missing.length === 0) {
@@ -7416,25 +7815,189 @@ ${c.bold("Missing parameters")} ${c.dim(`(${ctx.project} / ${ctx.stage})`)}
7416
7815
  );
7417
7816
  var configCommand = configRootCommand;
7418
7817
 
7818
+ // src/cli/commands/build.ts
7819
+ import { Args as Args4, Command as Command7, Options as Options5 } from "@effect/cli";
7820
+ import { Effect as Effect45, Console as Console9, Option as Option6 } from "effect";
7821
+ import * as path12 from "path";
7822
+ import * as fs8 from "fs";
7823
+ var buildFileArg = Args4.file({ name: "file", exists: "yes" }).pipe(
7824
+ Args4.withDescription("Handler file to build"),
7825
+ Args4.optional
7826
+ );
7827
+ var buildAllOption = Options5.boolean("all").pipe(
7828
+ Options5.withDescription("Build all exports from file")
7829
+ );
7830
+ var buildTableOption = Options5.boolean("table").pipe(
7831
+ Options5.withDescription("Build as table handler (defineTable)")
7832
+ );
7833
+ var buildCommand = Command7.make(
7834
+ "build",
7835
+ { file: buildFileArg, all: buildAllOption, table: buildTableOption, output: outputOption, verbose: verboseOption },
7836
+ ({ file, all, table, output, verbose }) => Effect45.gen(function* () {
7837
+ const { config, projectDir, cwd } = yield* ProjectConfig;
7838
+ const outputDir = path12.isAbsolute(output) ? output : path12.resolve(projectDir, output);
7839
+ const extraNodeModules = projectDir !== cwd ? [path12.join(projectDir, "node_modules")] : void 0;
7840
+ if (!fs8.existsSync(outputDir)) {
7841
+ fs8.mkdirSync(outputDir, { recursive: true });
7842
+ }
7843
+ const prodDeps = yield* readProductionDependencies(projectDir).pipe(
7844
+ Effect45.catchAll(() => Effect45.succeed([]))
7845
+ );
7846
+ const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect45.sync(() => collectLayerPackages(projectDir, prodDeps, extraNodeModules)) : { packages: [], warnings: [] };
7847
+ for (const warning of layerWarnings) {
7848
+ yield* Effect45.logWarning(`[layer] ${warning}`);
7849
+ }
7850
+ if (external.length > 0) {
7851
+ yield* Console9.log(`Using ${external.length} external packages (from layer)
7852
+ `);
7853
+ }
7854
+ yield* Option6.match(file, {
7855
+ onNone: () => Effect45.gen(function* () {
7856
+ const patterns = getPatternsFromConfig(config);
7857
+ if (!patterns) {
7858
+ yield* Console9.error("Error: No file specified and no 'handlers' patterns in config");
7859
+ return;
7860
+ }
7861
+ const files = findHandlerFiles(patterns, projectDir);
7862
+ const discovered = discoverHandlers(files);
7863
+ let builtCount = 0;
7864
+ for (const { file: handlerFile, exports } of discovered.apiHandlers) {
7865
+ const relativePath = path12.relative(projectDir, handlerFile);
7866
+ const baseName = path12.basename(handlerFile, path12.extname(handlerFile));
7867
+ for (const { exportName } of exports) {
7868
+ const outputName = exportName === "default" ? baseName : `${baseName}-${exportName}`;
7869
+ const outputPath = path12.join(outputDir, `${outputName}.mjs`);
7870
+ yield* Console9.log(`Building ${c.cyan("[api]")} ${c.bold(relativePath)}:${exportName}...`);
7871
+ const result = yield* bundle({
7872
+ projectDir,
7873
+ file: handlerFile,
7874
+ exportName,
7875
+ type: "api",
7876
+ ...external.length > 0 ? { external } : {}
7877
+ });
7878
+ fs8.writeFileSync(outputPath, result.code);
7879
+ const size = (Buffer.byteLength(result.code) / 1024).toFixed(1);
7880
+ yield* Console9.log(` -> ${outputPath} ${c.dim(`(${size} KB)`)}`);
7881
+ if (result.topModules) {
7882
+ for (const m of result.topModules.slice(0, 5)) {
7883
+ yield* Console9.log(` ${c.dim(`${(m.bytes / 1024).toFixed(1)} KB`)} ${c.dim(m.path)}`);
7884
+ }
7885
+ }
7886
+ builtCount++;
7887
+ }
7888
+ }
7889
+ for (const { file: handlerFile, exports } of discovered.tableHandlers) {
7890
+ const relativePath = path12.relative(projectDir, handlerFile);
7891
+ const baseName = path12.basename(handlerFile, path12.extname(handlerFile));
7892
+ for (const { exportName } of exports) {
7893
+ const outputName = exportName === "default" ? baseName : `${baseName}-${exportName}`;
7894
+ const outputPath = path12.join(outputDir, `${outputName}.mjs`);
7895
+ yield* Console9.log(`Building ${c.cyan("[table]")} ${c.bold(relativePath)}:${exportName}...`);
7896
+ const result = yield* bundle({
7897
+ projectDir,
7898
+ file: handlerFile,
7899
+ exportName,
7900
+ type: "table",
7901
+ ...external.length > 0 ? { external } : {}
7902
+ });
7903
+ fs8.writeFileSync(outputPath, result.code);
7904
+ const size = (Buffer.byteLength(result.code) / 1024).toFixed(1);
7905
+ yield* Console9.log(` -> ${outputPath} ${c.dim(`(${size} KB)`)}`);
7906
+ if (result.topModules) {
7907
+ for (const m of result.topModules.slice(0, 5)) {
7908
+ yield* Console9.log(` ${c.dim(`${(m.bytes / 1024).toFixed(1)} KB`)} ${c.dim(m.path)}`);
7909
+ }
7910
+ }
7911
+ builtCount++;
7912
+ }
7913
+ }
7914
+ yield* Console9.log(c.green(`
7915
+ Built ${builtCount} handler(s) to ${outputDir}`));
7916
+ }),
7917
+ onSome: (filePath) => Effect45.gen(function* () {
7918
+ const fullPath = path12.isAbsolute(filePath) ? filePath : path12.resolve(projectDir, filePath);
7919
+ const source = fs8.readFileSync(fullPath, "utf-8");
7920
+ const baseName = path12.basename(fullPath, path12.extname(fullPath));
7921
+ if (table) {
7922
+ const configs = extractTableConfigs(source);
7923
+ if (configs.length === 0) {
7924
+ yield* Console9.error("No defineTable handlers found in file");
7925
+ return;
7926
+ }
7927
+ const toBundle = all ? configs : [configs[0]];
7928
+ for (const { exportName } of toBundle) {
7929
+ const outputName = exportName === "default" ? baseName : `${baseName}-${exportName}`;
7930
+ const outputPath = path12.join(outputDir, `${outputName}.mjs`);
7931
+ yield* Console9.log(`Building ${c.cyan("[table]")} ${c.bold(exportName)}...`);
7932
+ const result = yield* bundle({
7933
+ projectDir,
7934
+ file: fullPath,
7935
+ exportName,
7936
+ type: "table",
7937
+ ...external.length > 0 ? { external } : {}
7938
+ });
7939
+ fs8.writeFileSync(outputPath, result.code);
7940
+ const size = (Buffer.byteLength(result.code) / 1024).toFixed(1);
7941
+ yield* Console9.log(` -> ${outputPath} ${c.dim(`(${size} KB)`)}`);
7942
+ if (result.topModules) {
7943
+ for (const m of result.topModules.slice(0, 5)) {
7944
+ yield* Console9.log(` ${c.dim(`${(m.bytes / 1024).toFixed(1)} KB`)} ${c.dim(m.path)}`);
7945
+ }
7946
+ }
7947
+ }
7948
+ } else {
7949
+ const configs = extractApiConfigs(source);
7950
+ if (configs.length === 0) {
7951
+ yield* Console9.error("No defineApi handlers found in file");
7952
+ return;
7953
+ }
7954
+ const toBundle = all ? configs : [configs[0]];
7955
+ for (const { exportName } of toBundle) {
7956
+ const outputName = exportName === "default" ? baseName : `${baseName}-${exportName}`;
7957
+ const outputPath = path12.join(outputDir, `${outputName}.mjs`);
7958
+ yield* Console9.log(`Building ${c.cyan("[api]")} ${c.bold(exportName)}...`);
7959
+ const result = yield* bundle({
7960
+ projectDir,
7961
+ file: fullPath,
7962
+ exportName,
7963
+ ...external.length > 0 ? { external } : {}
7964
+ });
7965
+ fs8.writeFileSync(outputPath, result.code);
7966
+ const size = (Buffer.byteLength(result.code) / 1024).toFixed(1);
7967
+ yield* Console9.log(` -> ${outputPath} ${c.dim(`(${size} KB)`)}`);
7968
+ if (result.topModules) {
7969
+ for (const m of result.topModules.slice(0, 5)) {
7970
+ yield* Console9.log(` ${c.dim(`${(m.bytes / 1024).toFixed(1)} KB`)} ${c.dim(m.path)}`);
7971
+ }
7972
+ }
7973
+ }
7974
+ }
7975
+ yield* Console9.log(`
7976
+ Output directory: ${outputDir}`);
7977
+ })
7978
+ });
7979
+ }).pipe(Effect45.provide(ProjectConfig.Live))
7980
+ ).pipe(Command7.withDescription("Build handler bundles locally (for debugging)"));
7981
+
7419
7982
  // src/cli/update-check.ts
7420
7983
  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";
7984
+ import { join as join11 } from "path";
7985
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
7423
7986
  var PACKAGE_NAME = "@effortless-aws/cli";
7424
7987
  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");
7988
+ var CACHE_DIR = join11(homedir2(), ".effortless-aws");
7989
+ var CACHE_FILE = join11(CACHE_DIR, "update-check.json");
7427
7990
  function readCache() {
7428
7991
  try {
7429
- return JSON.parse(readFileSync5(CACHE_FILE, "utf-8"));
7992
+ return JSON.parse(readFileSync6(CACHE_FILE, "utf-8"));
7430
7993
  } catch {
7431
7994
  return void 0;
7432
7995
  }
7433
7996
  }
7434
7997
  function writeCache(data) {
7435
7998
  try {
7436
- mkdirSync2(CACHE_DIR, { recursive: true });
7437
- writeFileSync2(CACHE_FILE, JSON.stringify(data));
7999
+ mkdirSync3(CACHE_DIR, { recursive: true });
8000
+ writeFileSync3(CACHE_FILE, JSON.stringify(data));
7438
8001
  } catch {
7439
8002
  }
7440
8003
  }
@@ -7496,18 +8059,18 @@ async function checkForUpdate(currentVersion) {
7496
8059
  // src/cli/index.ts
7497
8060
  var require2 = createRequire2(import.meta.url);
7498
8061
  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")
8062
+ var mainCommand = Command8.make("eff").pipe(
8063
+ Command8.withSubcommands([deployCommand, statusCommand, logsCommand, cleanupCommand, layerCommand, configCommand, buildCommand]),
8064
+ Command8.withDescription("Code-first AWS Lambda framework")
7502
8065
  );
7503
- var cli = Command7.run(mainCommand, {
8066
+ var cli = Command8.run(mainCommand, {
7504
8067
  name: "effortless",
7505
8068
  version
7506
8069
  });
7507
8070
  var updateCheck = checkForUpdate(version);
7508
8071
  cli(process.argv).pipe(
7509
- Effect45.provide(NodeContext.layer),
7510
- Effect45.provide(CliConfig.layer({ showBuiltIns: false, showTypes: false })),
7511
- Effect45.tap(() => Effect45.promise(() => updateCheck)),
8072
+ Effect46.provide(NodeContext.layer),
8073
+ Effect46.provide(CliConfig.layer({ showBuiltIns: false, showTypes: false })),
8074
+ Effect46.tap(() => Effect46.promise(() => updateCheck)),
7512
8075
  NodeRuntime.runMain
7513
8076
  );