@effortless-aws/cli 0.7.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.
- package/dist/cli/index.js +932 -219
- 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
|
|
9
|
+
import { CliConfig, Command as Command8 } from "@effect/cli";
|
|
10
10
|
import { NodeContext, NodeRuntime } from "@effect/platform-node";
|
|
11
|
-
import { Effect as
|
|
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(
|
|
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
|
|
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.
|
|
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(", ")})
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
4031
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
4067
|
-
|
|
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
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
}
|
|
4085
|
-
|
|
4086
|
-
|
|
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
|
-
|
|
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
|
|
4104
|
-
if (
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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,53 +4351,56 @@ var handlerRegistry = {
|
|
|
4190
4351
|
var extractHandlerConfigs = (source, type) => {
|
|
4191
4352
|
const { defineFn, handlerProps } = handlerRegistry[type];
|
|
4192
4353
|
const sourceFile = parseSource(source);
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
const
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
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
|
+
};
|
|
4368
|
+
});
|
|
4369
|
+
};
|
|
4370
|
+
var generateMiddlewareEntryPoint = (source, runtimeDir2) => {
|
|
4371
|
+
const sourceFile = parseSource(source);
|
|
4372
|
+
const calls = findDefineCalls(sourceFile, handlerRegistry.staticSite.defineFn);
|
|
4373
|
+
let middlewareFnText;
|
|
4374
|
+
let exportName;
|
|
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;
|
|
4214
4381
|
}
|
|
4215
4382
|
}
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
const
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
});
|
|
4238
|
-
});
|
|
4239
|
-
return results;
|
|
4383
|
+
if (!middlewareFnText || !exportName) {
|
|
4384
|
+
throw new Error("Could not extract middleware function from source");
|
|
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");
|
|
4397
|
+
const wrapperPath = runtimeDir2 ? handlerRegistry.staticSite.wrapperPath.replace("~/runtime", runtimeDir2) : handlerRegistry.staticSite.wrapperPath;
|
|
4398
|
+
const entryPoint = `${imports}
|
|
4399
|
+
import { wrapMiddlewareFn } from "${wrapperPath}";
|
|
4400
|
+
const __middleware = ${middlewareFnText};
|
|
4401
|
+
export const handler = wrapMiddlewareFn(__middleware);
|
|
4402
|
+
`;
|
|
4403
|
+
return { entryPoint, exportName };
|
|
4240
4404
|
};
|
|
4241
4405
|
var generateEntryPoint = (sourcePath, exportName, type, runtimeDir2) => {
|
|
4242
4406
|
const { wrapperFn, wrapperPath } = handlerRegistry[type];
|
|
@@ -4250,6 +4414,7 @@ export const handler = ${wrapperFn}(${importName});
|
|
|
4250
4414
|
};
|
|
4251
4415
|
|
|
4252
4416
|
// src/build/bundle.ts
|
|
4417
|
+
var AUTH_COOKIE_NAME = "__eff_session";
|
|
4253
4418
|
var extractTableConfigs = (source) => extractHandlerConfigs(source, "table");
|
|
4254
4419
|
var extractAppConfigs = (source) => extractHandlerConfigs(source, "app");
|
|
4255
4420
|
var extractStaticSiteConfigs = (source) => extractHandlerConfigs(source, "staticSite");
|
|
@@ -4266,7 +4431,8 @@ var bundle = (input) => Effect24.gen(function* () {
|
|
|
4266
4431
|
const sourcePath = path3.isAbsolute(input.file) ? input.file : `./${input.file}`;
|
|
4267
4432
|
const entryPoint = generateEntryPoint(sourcePath, exportName, type, runtimeDir);
|
|
4268
4433
|
const awsExternals = ["@aws-sdk/*", "@smithy/*"];
|
|
4269
|
-
const
|
|
4434
|
+
const nodeExternals = builtinModules.flatMap((m) => [m, `node:${m}`]);
|
|
4435
|
+
const allExternals = [.../* @__PURE__ */ new Set([...awsExternals, ...nodeExternals, ...externals])];
|
|
4270
4436
|
const result = yield* Effect24.tryPromise({
|
|
4271
4437
|
try: () => esbuild.build({
|
|
4272
4438
|
stdin: {
|
|
@@ -4281,7 +4447,8 @@ var bundle = (input) => Effect24.gen(function* () {
|
|
|
4281
4447
|
minify: false,
|
|
4282
4448
|
sourcemap: false,
|
|
4283
4449
|
format: input.format ?? "esm",
|
|
4284
|
-
external: allExternals
|
|
4450
|
+
external: allExternals,
|
|
4451
|
+
metafile: true
|
|
4285
4452
|
}),
|
|
4286
4453
|
catch: (error) => new Error(`esbuild failed: ${error}`)
|
|
4287
4454
|
});
|
|
@@ -4289,6 +4456,160 @@ var bundle = (input) => Effect24.gen(function* () {
|
|
|
4289
4456
|
if (!output) {
|
|
4290
4457
|
throw new Error("esbuild produced no output");
|
|
4291
4458
|
}
|
|
4459
|
+
const bundleResult = { code: output.text };
|
|
4460
|
+
if (result.metafile) {
|
|
4461
|
+
bundleResult.topModules = analyzeMetafile(result.metafile);
|
|
4462
|
+
}
|
|
4463
|
+
return bundleResult;
|
|
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
|
+
};
|
|
4485
|
+
var bundleMiddleware = (input) => Effect24.gen(function* () {
|
|
4486
|
+
const absFile = path3.isAbsolute(input.file) ? input.file : path3.resolve(input.projectDir, input.file);
|
|
4487
|
+
const source = fsSync2.readFileSync(absFile, "utf-8");
|
|
4488
|
+
const sourceDir = path3.dirname(absFile);
|
|
4489
|
+
const { entryPoint } = generateMiddlewareEntryPoint(source, runtimeDir);
|
|
4490
|
+
const awsExternals = ["@aws-sdk/*", "@smithy/*"];
|
|
4491
|
+
const result = yield* Effect24.tryPromise({
|
|
4492
|
+
try: () => esbuild.build({
|
|
4493
|
+
stdin: {
|
|
4494
|
+
contents: entryPoint,
|
|
4495
|
+
loader: "ts",
|
|
4496
|
+
resolveDir: sourceDir
|
|
4497
|
+
},
|
|
4498
|
+
bundle: true,
|
|
4499
|
+
platform: "node",
|
|
4500
|
+
target: "node22",
|
|
4501
|
+
write: false,
|
|
4502
|
+
minify: false,
|
|
4503
|
+
sourcemap: false,
|
|
4504
|
+
format: "esm",
|
|
4505
|
+
external: awsExternals
|
|
4506
|
+
}),
|
|
4507
|
+
catch: (error) => new Error(`esbuild failed (middleware): ${error}`)
|
|
4508
|
+
});
|
|
4509
|
+
const output = result.outputFiles?.[0];
|
|
4510
|
+
if (!output) {
|
|
4511
|
+
throw new Error("esbuild produced no output for middleware");
|
|
4512
|
+
}
|
|
4513
|
+
return output.text;
|
|
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");
|
|
4292
4613
|
return output.text;
|
|
4293
4614
|
});
|
|
4294
4615
|
var FIXED_DATE2 = /* @__PURE__ */ new Date(0);
|
|
@@ -4390,21 +4711,25 @@ var flattenHandlers = (discovered) => {
|
|
|
4390
4711
|
];
|
|
4391
4712
|
};
|
|
4392
4713
|
|
|
4714
|
+
// src/deploy/deploy.ts
|
|
4715
|
+
import * as crypto5 from "crypto";
|
|
4716
|
+
|
|
4393
4717
|
// src/deploy/resolve-config.ts
|
|
4394
4718
|
import { Effect as Effect25 } from "effect";
|
|
4395
|
-
var
|
|
4719
|
+
var collectRequiredSecrets = (handlers, project, stage) => {
|
|
4396
4720
|
const seen = /* @__PURE__ */ new Map();
|
|
4397
4721
|
const collect = (handlerGroups) => {
|
|
4398
4722
|
for (const { exports } of handlerGroups) {
|
|
4399
4723
|
for (const fn13 of exports) {
|
|
4400
|
-
for (const { propName, ssmKey } of fn13.
|
|
4724
|
+
for (const { propName, ssmKey, generate } of fn13.secretEntries) {
|
|
4401
4725
|
const ssmPath = `/${project}/${stage}/${ssmKey}`;
|
|
4402
4726
|
if (!seen.has(ssmPath)) {
|
|
4403
4727
|
seen.set(ssmPath, {
|
|
4404
4728
|
ssmPath,
|
|
4405
4729
|
propName,
|
|
4406
4730
|
ssmKey,
|
|
4407
|
-
handlerName: fn13.exportName
|
|
4731
|
+
handlerName: fn13.exportName,
|
|
4732
|
+
...generate ? { generate } : {}
|
|
4408
4733
|
});
|
|
4409
4734
|
}
|
|
4410
4735
|
}
|
|
@@ -4417,11 +4742,34 @@ var collectRequiredParams = (handlers, project, stage) => {
|
|
|
4417
4742
|
collect(handlers.apiHandlers);
|
|
4418
4743
|
return Array.from(seen.values());
|
|
4419
4744
|
};
|
|
4420
|
-
var
|
|
4421
|
-
|
|
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: [] };
|
|
4422
4770
|
const existingNames = /* @__PURE__ */ new Set();
|
|
4423
|
-
for (let i = 0; i <
|
|
4424
|
-
const batch =
|
|
4771
|
+
for (let i = 0; i < secrets.length; i += 10) {
|
|
4772
|
+
const batch = secrets.slice(i, i + 10);
|
|
4425
4773
|
const result = yield* ssm_exports.make("get_parameters", {
|
|
4426
4774
|
Names: batch.map((p) => p.ssmPath),
|
|
4427
4775
|
WithDecryption: false
|
|
@@ -4432,7 +4780,7 @@ var checkMissingParams = (params) => Effect25.gen(function* () {
|
|
|
4432
4780
|
}
|
|
4433
4781
|
const existing = [];
|
|
4434
4782
|
const missing = [];
|
|
4435
|
-
for (const p of
|
|
4783
|
+
for (const p of secrets) {
|
|
4436
4784
|
if (existingNames.has(p.ssmPath)) {
|
|
4437
4785
|
existing.push(p);
|
|
4438
4786
|
} else {
|
|
@@ -4450,6 +4798,12 @@ import { toSeconds } from "effortless-aws";
|
|
|
4450
4798
|
import { Effect as Effect26 } from "effect";
|
|
4451
4799
|
import * as fs3 from "fs/promises";
|
|
4452
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
|
+
};
|
|
4453
4807
|
var readSource = (input) => Effect26.gen(function* () {
|
|
4454
4808
|
if ("code" in input && typeof input.code === "string") {
|
|
4455
4809
|
return input.code;
|
|
@@ -4462,10 +4816,11 @@ var ensureLayerAndExternal = (input) => Effect26.gen(function* () {
|
|
|
4462
4816
|
project: input.project,
|
|
4463
4817
|
stage: input.stage,
|
|
4464
4818
|
region: input.region,
|
|
4465
|
-
projectDir: input.packageDir
|
|
4819
|
+
projectDir: input.packageDir,
|
|
4820
|
+
extraNodeModules: input.extraNodeModules
|
|
4466
4821
|
});
|
|
4467
4822
|
const prodDeps = layerResult ? yield* readProductionDependencies(input.packageDir) : [];
|
|
4468
|
-
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: [] };
|
|
4469
4824
|
for (const warning of layerWarnings) {
|
|
4470
4825
|
yield* Effect26.logWarning(`[layer] ${warning}`);
|
|
4471
4826
|
}
|
|
@@ -4510,14 +4865,21 @@ var deployCoreLambda = ({
|
|
|
4510
4865
|
mergedPermissions.length > 0 ? mergedPermissions : void 0,
|
|
4511
4866
|
makeTags(tagCtx, "iam-role")
|
|
4512
4867
|
);
|
|
4513
|
-
const
|
|
4868
|
+
const bundleResult = yield* bundle({
|
|
4514
4869
|
...input,
|
|
4515
4870
|
exportName,
|
|
4516
4871
|
...bundleType ? { type: bundleType } : {},
|
|
4517
4872
|
...external && external.length > 0 ? { external } : {}
|
|
4518
4873
|
});
|
|
4519
4874
|
const staticFiles = staticGlobs && staticGlobs.length > 0 ? resolveStaticFiles(staticGlobs, input.projectDir) : void 0;
|
|
4520
|
-
const
|
|
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 });
|
|
4521
4883
|
const environment = {
|
|
4522
4884
|
EFF_PROJECT: input.project,
|
|
4523
4885
|
EFF_STAGE: tagCtx.stage,
|
|
@@ -4537,7 +4899,7 @@ var deployCoreLambda = ({
|
|
|
4537
4899
|
...layerArn ? { layers: [layerArn] } : {},
|
|
4538
4900
|
environment
|
|
4539
4901
|
});
|
|
4540
|
-
return { functionArn, status, tagCtx };
|
|
4902
|
+
return { functionArn, status, tagCtx, bundleSize };
|
|
4541
4903
|
});
|
|
4542
4904
|
|
|
4543
4905
|
// src/deploy/deploy-table.ts
|
|
@@ -4559,7 +4921,7 @@ var deployTableFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsP
|
|
|
4559
4921
|
tags: makeTags(tagCtx, "dynamodb")
|
|
4560
4922
|
});
|
|
4561
4923
|
const selfEnv = { EFF_DEP_SELF: `table:${tableName}`, ...depsEnv };
|
|
4562
|
-
const { functionArn, status } = yield* deployCoreLambda({
|
|
4924
|
+
const { functionArn, status, bundleSize } = yield* deployCoreLambda({
|
|
4563
4925
|
input,
|
|
4564
4926
|
exportName,
|
|
4565
4927
|
handlerName,
|
|
@@ -4587,6 +4949,7 @@ var deployTableFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsP
|
|
|
4587
4949
|
exportName,
|
|
4588
4950
|
functionArn,
|
|
4589
4951
|
status,
|
|
4952
|
+
bundleSize,
|
|
4590
4953
|
tableArn,
|
|
4591
4954
|
streamArn
|
|
4592
4955
|
};
|
|
@@ -4603,7 +4966,8 @@ var deployTable = (input) => Effect27.gen(function* () {
|
|
|
4603
4966
|
project: input.project,
|
|
4604
4967
|
stage: resolveStage(input.stage),
|
|
4605
4968
|
region: input.region,
|
|
4606
|
-
packageDir: input.packageDir ?? input.projectDir
|
|
4969
|
+
packageDir: input.packageDir ?? input.projectDir,
|
|
4970
|
+
extraNodeModules: input.extraNodeModules
|
|
4607
4971
|
});
|
|
4608
4972
|
const result = yield* deployTableFunction({
|
|
4609
4973
|
input,
|
|
@@ -4632,7 +4996,8 @@ var deployAllTables = (input) => Effect27.gen(function* () {
|
|
|
4632
4996
|
project: input.project,
|
|
4633
4997
|
stage: resolveStage(input.stage),
|
|
4634
4998
|
region: input.region,
|
|
4635
|
-
packageDir: input.packageDir ?? input.projectDir
|
|
4999
|
+
packageDir: input.packageDir ?? input.projectDir,
|
|
5000
|
+
extraNodeModules: input.extraNodeModules
|
|
4636
5001
|
});
|
|
4637
5002
|
const results = yield* Effect27.forEach(
|
|
4638
5003
|
functions,
|
|
@@ -4661,7 +5026,7 @@ import { toSeconds as toSeconds2 } from "effortless-aws";
|
|
|
4661
5026
|
var deployApiFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPermissions, staticGlobs }) => Effect28.gen(function* () {
|
|
4662
5027
|
const { exportName, config } = fn13;
|
|
4663
5028
|
const handlerName = exportName;
|
|
4664
|
-
const { functionArn, status } = yield* deployCoreLambda({
|
|
5029
|
+
const { functionArn, status, bundleSize } = yield* deployCoreLambda({
|
|
4665
5030
|
input,
|
|
4666
5031
|
exportName,
|
|
4667
5032
|
handlerName,
|
|
@@ -4675,7 +5040,7 @@ var deployApiFunction = ({ input, fn: fn13, layerArn, external, depsEnv, depsPer
|
|
|
4675
5040
|
...depsPermissions ? { depsPermissions } : {},
|
|
4676
5041
|
...staticGlobs && staticGlobs.length > 0 ? { staticGlobs } : {}
|
|
4677
5042
|
});
|
|
4678
|
-
return { exportName, functionArn, status, config, handlerName };
|
|
5043
|
+
return { exportName, functionArn, status, bundleSize, config, handlerName };
|
|
4679
5044
|
});
|
|
4680
5045
|
var deploy = (input) => Effect28.gen(function* () {
|
|
4681
5046
|
const source = yield* readSource(input);
|
|
@@ -4696,7 +5061,8 @@ var deploy = (input) => Effect28.gen(function* () {
|
|
|
4696
5061
|
project: input.project,
|
|
4697
5062
|
stage: tagCtx.stage,
|
|
4698
5063
|
region: input.region,
|
|
4699
|
-
packageDir: input.packageDir ?? input.projectDir
|
|
5064
|
+
packageDir: input.packageDir ?? input.projectDir,
|
|
5065
|
+
extraNodeModules: input.extraNodeModules
|
|
4700
5066
|
});
|
|
4701
5067
|
const { functionArn } = yield* deployApiFunction({
|
|
4702
5068
|
input,
|
|
@@ -5048,7 +5414,7 @@ var submitToGoogleIndexing = (input) => Effect30.gen(function* () {
|
|
|
5048
5414
|
|
|
5049
5415
|
// src/deploy/deploy-static-site.ts
|
|
5050
5416
|
var deployMiddlewareLambda = (input) => Effect31.gen(function* () {
|
|
5051
|
-
const { projectDir, project, stage, handlerName, file,
|
|
5417
|
+
const { projectDir, project, stage, handlerName, file, tagCtx } = input;
|
|
5052
5418
|
const middlewareName = `${handlerName}-middleware`;
|
|
5053
5419
|
yield* Effect31.logDebug(`Deploying middleware Lambda@Edge: ${middlewareName}`);
|
|
5054
5420
|
const roleArn = yield* ensureEdgeRole(
|
|
@@ -5057,12 +5423,13 @@ var deployMiddlewareLambda = (input) => Effect31.gen(function* () {
|
|
|
5057
5423
|
middlewareName,
|
|
5058
5424
|
makeTags(tagCtx, "iam-role")
|
|
5059
5425
|
);
|
|
5060
|
-
const bundled = yield*
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
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
|
+
}
|
|
5066
5433
|
const code = yield* zip({ content: bundled });
|
|
5067
5434
|
const { functionArn } = yield* ensureLambda({
|
|
5068
5435
|
project,
|
|
@@ -5078,14 +5445,52 @@ var deployMiddlewareLambda = (input) => Effect31.gen(function* () {
|
|
|
5078
5445
|
}).pipe(
|
|
5079
5446
|
Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
|
|
5080
5447
|
);
|
|
5081
|
-
const
|
|
5082
|
-
|
|
5083
|
-
|
|
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(
|
|
5084
5453
|
Effect31.provide(clients_exports.makeClients({ lambda: { region: "us-east-1" } }))
|
|
5085
5454
|
);
|
|
5086
5455
|
yield* Effect31.logDebug(`Middleware deployed: ${versionArn}`);
|
|
5087
5456
|
return { versionArn };
|
|
5088
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
|
+
});
|
|
5089
5494
|
var ERROR_PAGE_KEY = "_effortless/404.html";
|
|
5090
5495
|
var generateErrorPageHtml = () => `<!DOCTYPE html>
|
|
5091
5496
|
<html lang="en">
|
|
@@ -5190,14 +5595,26 @@ var deployStaticSite = (input) => Effect31.gen(function* () {
|
|
|
5190
5595
|
const isSpa = config.spa ?? false;
|
|
5191
5596
|
let urlRewriteFunctionArn;
|
|
5192
5597
|
let lambdaEdgeArn;
|
|
5193
|
-
|
|
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) {
|
|
5194
5612
|
const result = yield* deployMiddlewareLambda({
|
|
5195
5613
|
projectDir,
|
|
5196
5614
|
project,
|
|
5197
5615
|
stage,
|
|
5198
5616
|
handlerName,
|
|
5199
5617
|
file: input.file,
|
|
5200
|
-
exportName,
|
|
5201
5618
|
tagCtx
|
|
5202
5619
|
}).pipe(
|
|
5203
5620
|
Effect31.provide(clients_exports.makeClients({ iam: { region: "us-east-1" } }))
|
|
@@ -5321,7 +5738,7 @@ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, d
|
|
|
5321
5738
|
EFF_QUEUE_ARN: queueArn,
|
|
5322
5739
|
...depsEnv
|
|
5323
5740
|
};
|
|
5324
|
-
const { functionArn, status } = yield* deployCoreLambda({
|
|
5741
|
+
const { functionArn, status, bundleSize } = yield* deployCoreLambda({
|
|
5325
5742
|
input,
|
|
5326
5743
|
exportName,
|
|
5327
5744
|
handlerName,
|
|
@@ -5348,6 +5765,7 @@ var deployFifoQueueFunction = ({ input, fn: fn13, layerArn, external, depsEnv, d
|
|
|
5348
5765
|
exportName,
|
|
5349
5766
|
functionArn,
|
|
5350
5767
|
status,
|
|
5768
|
+
bundleSize,
|
|
5351
5769
|
queueUrl,
|
|
5352
5770
|
queueArn
|
|
5353
5771
|
};
|
|
@@ -5382,7 +5800,7 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
|
|
|
5382
5800
|
};
|
|
5383
5801
|
}
|
|
5384
5802
|
const selfEnv = { EFF_DEP_SELF: `bucket:${bucketName}`, ...depsEnv };
|
|
5385
|
-
const { functionArn, status } = yield* deployCoreLambda({
|
|
5803
|
+
const { functionArn, status, bundleSize } = yield* deployCoreLambda({
|
|
5386
5804
|
input,
|
|
5387
5805
|
exportName,
|
|
5388
5806
|
handlerName,
|
|
@@ -5410,6 +5828,7 @@ var deployBucketFunction = ({ input, fn: fn13, layerArn, external, depsEnv, deps
|
|
|
5410
5828
|
exportName,
|
|
5411
5829
|
functionArn,
|
|
5412
5830
|
status,
|
|
5831
|
+
bundleSize,
|
|
5413
5832
|
bucketName,
|
|
5414
5833
|
bucketArn
|
|
5415
5834
|
};
|
|
@@ -5490,10 +5909,17 @@ var createLiveProgress = (manifest) => {
|
|
|
5490
5909
|
const sec = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
5491
5910
|
return c.dim(`${sec}s`);
|
|
5492
5911
|
};
|
|
5493
|
-
|
|
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(() => {
|
|
5494
5919
|
const key = `${name}:${type}`;
|
|
5495
5920
|
results.set(key, status);
|
|
5496
|
-
const
|
|
5921
|
+
const sizeInfo = bundleSize ? ` ${c.dim(formatSize(bundleSize))}` : "";
|
|
5922
|
+
const line = ` ${name} ${c.dim(`(${type})`)} ${statusLabel(status)}${sizeInfo} ${formatDuration()}`;
|
|
5497
5923
|
if (isTTY) {
|
|
5498
5924
|
const idx = lineIndex.get(key) ?? 0;
|
|
5499
5925
|
const up = manifest.length - idx;
|
|
@@ -5502,7 +5928,7 @@ var createLiveProgress = (manifest) => {
|
|
|
5502
5928
|
clearInterval(timer);
|
|
5503
5929
|
}
|
|
5504
5930
|
} else {
|
|
5505
|
-
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()}
|
|
5506
5932
|
`);
|
|
5507
5933
|
}
|
|
5508
5934
|
});
|
|
@@ -5513,7 +5939,8 @@ var prepareLayer = (input) => Effect35.gen(function* () {
|
|
|
5513
5939
|
project: input.project,
|
|
5514
5940
|
stage: input.stage,
|
|
5515
5941
|
region: input.region,
|
|
5516
|
-
projectDir: input.packageDir
|
|
5942
|
+
projectDir: input.packageDir,
|
|
5943
|
+
extraNodeModules: input.extraNodeModules
|
|
5517
5944
|
}).pipe(
|
|
5518
5945
|
Effect35.provide(
|
|
5519
5946
|
clients_exports.makeClients({
|
|
@@ -5522,7 +5949,7 @@ var prepareLayer = (input) => Effect35.gen(function* () {
|
|
|
5522
5949
|
)
|
|
5523
5950
|
);
|
|
5524
5951
|
const prodDeps = layerResult ? yield* readProductionDependencies(input.packageDir) : [];
|
|
5525
|
-
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: [] };
|
|
5526
5953
|
for (const warning of layerWarnings) {
|
|
5527
5954
|
yield* Effect35.logWarning(`[layer] ${warning}`);
|
|
5528
5955
|
}
|
|
@@ -5668,10 +6095,20 @@ var SSM_PERMISSIONS = [
|
|
|
5668
6095
|
"ssm:GetParameter",
|
|
5669
6096
|
"ssm:GetParameters"
|
|
5670
6097
|
];
|
|
5671
|
-
var
|
|
5672
|
-
|
|
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;
|
|
5673
6110
|
const paramsEnv = {};
|
|
5674
|
-
for (const { propName, ssmKey } of
|
|
6111
|
+
for (const { propName, ssmKey } of secretEntries) {
|
|
5675
6112
|
paramsEnv[`EFF_PARAM_${propName}`] = `/${project}/${stage}/${ssmKey}`;
|
|
5676
6113
|
}
|
|
5677
6114
|
return { paramsEnv, paramsPermissions: SSM_PERMISSIONS };
|
|
@@ -5690,10 +6127,10 @@ var makeDeployInput = (ctx, file) => ({
|
|
|
5690
6127
|
region: ctx.input.region,
|
|
5691
6128
|
...ctx.input.stage ? { stage: ctx.input.stage } : {}
|
|
5692
6129
|
});
|
|
5693
|
-
var resolveHandlerEnv = (depsKeys,
|
|
6130
|
+
var resolveHandlerEnv = (depsKeys, secretEntries, ctx) => {
|
|
5694
6131
|
const resolved = mergeResolved(
|
|
5695
6132
|
resolveDeps(depsKeys, ctx.tableNameMap, ctx.bucketNameMap, ctx.mailerDomainMap, ctx.queueNameMap),
|
|
5696
|
-
|
|
6133
|
+
resolveSecrets(secretEntries, ctx.input.project, ctx.stage)
|
|
5697
6134
|
);
|
|
5698
6135
|
return {
|
|
5699
6136
|
depsEnv: resolved?.depsEnv ?? {},
|
|
@@ -5707,7 +6144,7 @@ var buildTableTasks = (ctx, handlers, results) => {
|
|
|
5707
6144
|
for (const fn13 of exports) {
|
|
5708
6145
|
tasks.push(
|
|
5709
6146
|
Effect35.gen(function* () {
|
|
5710
|
-
const env = resolveHandlerEnv(fn13.depsKeys, fn13.
|
|
6147
|
+
const env = resolveHandlerEnv(fn13.depsKeys, fn13.secretEntries, ctx);
|
|
5711
6148
|
const result = yield* deployTableFunction({
|
|
5712
6149
|
input: makeDeployInput(ctx, file),
|
|
5713
6150
|
fn: fn13,
|
|
@@ -5718,7 +6155,7 @@ var buildTableTasks = (ctx, handlers, results) => {
|
|
|
5718
6155
|
...fn13.staticGlobs.length > 0 ? { staticGlobs: fn13.staticGlobs } : {}
|
|
5719
6156
|
}).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, dynamodb: { region } })));
|
|
5720
6157
|
results.push(result);
|
|
5721
|
-
yield* ctx.logComplete(fn13.exportName, "table", result.status);
|
|
6158
|
+
yield* ctx.logComplete(fn13.exportName, "table", result.status, result.bundleSize);
|
|
5722
6159
|
})
|
|
5723
6160
|
);
|
|
5724
6161
|
}
|
|
@@ -5763,6 +6200,12 @@ var buildStaticSiteTasks = (ctx, handlers, results, apiOriginDomain) => {
|
|
|
5763
6200
|
for (const fn13 of exports) {
|
|
5764
6201
|
tasks.push(
|
|
5765
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
|
+
}
|
|
5766
6209
|
const result = yield* deployStaticSite({
|
|
5767
6210
|
projectDir: ctx.input.projectDir,
|
|
5768
6211
|
project: ctx.input.project,
|
|
@@ -5771,7 +6214,8 @@ var buildStaticSiteTasks = (ctx, handlers, results, apiOriginDomain) => {
|
|
|
5771
6214
|
fn: fn13,
|
|
5772
6215
|
verbose: ctx.input.verbose,
|
|
5773
6216
|
...fn13.hasHandler ? { file } : {},
|
|
5774
|
-
...apiOriginDomain ? { apiOriginDomain } : {}
|
|
6217
|
+
...apiOriginDomain ? { apiOriginDomain } : {},
|
|
6218
|
+
...authSecretValue && fn13.authConfig ? { authSecret: authSecretValue, authConfig: fn13.authConfig } : {}
|
|
5775
6219
|
}).pipe(Effect35.provide(clients_exports.makeClients({
|
|
5776
6220
|
s3: { region },
|
|
5777
6221
|
cloudfront: { region: "us-east-1" },
|
|
@@ -5793,7 +6237,7 @@ var buildFifoQueueTasks = (ctx, handlers, results) => {
|
|
|
5793
6237
|
for (const fn13 of exports) {
|
|
5794
6238
|
tasks.push(
|
|
5795
6239
|
Effect35.gen(function* () {
|
|
5796
|
-
const env = resolveHandlerEnv(fn13.depsKeys, fn13.
|
|
6240
|
+
const env = resolveHandlerEnv(fn13.depsKeys, fn13.secretEntries, ctx);
|
|
5797
6241
|
const result = yield* deployFifoQueueFunction({
|
|
5798
6242
|
input: makeDeployInput(ctx, file),
|
|
5799
6243
|
fn: fn13,
|
|
@@ -5804,7 +6248,7 @@ var buildFifoQueueTasks = (ctx, handlers, results) => {
|
|
|
5804
6248
|
...fn13.staticGlobs.length > 0 ? { staticGlobs: fn13.staticGlobs } : {}
|
|
5805
6249
|
}).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, sqs: { region } })));
|
|
5806
6250
|
results.push(result);
|
|
5807
|
-
yield* ctx.logComplete(fn13.exportName, "queue", result.status);
|
|
6251
|
+
yield* ctx.logComplete(fn13.exportName, "queue", result.status, result.bundleSize);
|
|
5808
6252
|
})
|
|
5809
6253
|
);
|
|
5810
6254
|
}
|
|
@@ -5818,7 +6262,7 @@ var buildBucketTasks = (ctx, handlers, results) => {
|
|
|
5818
6262
|
for (const fn13 of exports) {
|
|
5819
6263
|
tasks.push(
|
|
5820
6264
|
Effect35.gen(function* () {
|
|
5821
|
-
const env = resolveHandlerEnv(fn13.depsKeys, fn13.
|
|
6265
|
+
const env = resolveHandlerEnv(fn13.depsKeys, fn13.secretEntries, ctx);
|
|
5822
6266
|
const result = yield* deployBucketFunction({
|
|
5823
6267
|
input: makeDeployInput(ctx, file),
|
|
5824
6268
|
fn: fn13,
|
|
@@ -5830,7 +6274,7 @@ var buildBucketTasks = (ctx, handlers, results) => {
|
|
|
5830
6274
|
}).pipe(Effect35.provide(clients_exports.makeClients({ lambda: { region }, iam: { region }, s3: { region } })));
|
|
5831
6275
|
results.push(result);
|
|
5832
6276
|
const status = result.status === "resource-only" ? "created" : result.status;
|
|
5833
|
-
yield* ctx.logComplete(fn13.exportName, "bucket", status);
|
|
6277
|
+
yield* ctx.logComplete(fn13.exportName, "bucket", status, result.bundleSize);
|
|
5834
6278
|
})
|
|
5835
6279
|
);
|
|
5836
6280
|
}
|
|
@@ -5865,8 +6309,12 @@ var buildApiTasks = (ctx, handlers, results) => {
|
|
|
5865
6309
|
for (const fn13 of exports) {
|
|
5866
6310
|
tasks.push(
|
|
5867
6311
|
Effect35.gen(function* () {
|
|
5868
|
-
const env = resolveHandlerEnv(fn13.depsKeys, fn13.
|
|
5869
|
-
|
|
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({
|
|
5870
6318
|
input: makeDeployInput(ctx, file),
|
|
5871
6319
|
fn: fn13,
|
|
5872
6320
|
...ctx.layerArn ? { layerArn: ctx.layerArn } : {},
|
|
@@ -5883,7 +6331,7 @@ var buildApiTasks = (ctx, handlers, results) => {
|
|
|
5883
6331
|
Effect35.provide(clients_exports.makeClients({ lambda: { region } }))
|
|
5884
6332
|
);
|
|
5885
6333
|
results.push({ exportName, url: functionUrl, functionArn });
|
|
5886
|
-
yield* ctx.logComplete(exportName, "api", status);
|
|
6334
|
+
yield* ctx.logComplete(exportName, "api", status, bundleSize);
|
|
5887
6335
|
})
|
|
5888
6336
|
);
|
|
5889
6337
|
}
|
|
@@ -5920,16 +6368,33 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
5920
6368
|
yield* Console2.log(`
|
|
5921
6369
|
${c.dim("Handlers:")} ${parts.join(", ")}`);
|
|
5922
6370
|
const discovered = { tableHandlers, appHandlers, staticSiteHandlers, fifoQueueHandlers, bucketHandlers, mailerHandlers, apiHandlers };
|
|
5923
|
-
const
|
|
5924
|
-
|
|
5925
|
-
|
|
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(
|
|
5926
6376
|
Effect35.provide(clients_exports.makeClients({ ssm: { region: input.region } }))
|
|
5927
6377
|
);
|
|
5928
|
-
|
|
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
|
+
}
|
|
6390
|
+
yield* Console2.log(`
|
|
6391
|
+
${c.green("\u2713")} Auto-created ${withGenerators.length} secret(s)`);
|
|
6392
|
+
}
|
|
6393
|
+
if (manualOnly.length > 0) {
|
|
5929
6394
|
yield* Console2.log(`
|
|
5930
|
-
${c.yellow("\u26A0")} Missing ${
|
|
6395
|
+
${c.yellow("\u26A0")} Missing ${manualOnly.length} SSM parameter(s):
|
|
5931
6396
|
`);
|
|
5932
|
-
for (const p of
|
|
6397
|
+
for (const p of manualOnly) {
|
|
5933
6398
|
yield* Console2.log(` ${c.dim(p.handlerName)} \u2192 ${c.yellow(p.ssmPath)}`);
|
|
5934
6399
|
}
|
|
5935
6400
|
yield* Console2.log(`
|
|
@@ -5953,7 +6418,8 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
5953
6418
|
project: input.project,
|
|
5954
6419
|
stage,
|
|
5955
6420
|
region: input.region,
|
|
5956
|
-
packageDir: input.packageDir ?? input.projectDir
|
|
6421
|
+
packageDir: input.packageDir ?? input.projectDir,
|
|
6422
|
+
extraNodeModules: input.extraNodeModules
|
|
5957
6423
|
}) : { layerArn: void 0, layerVersion: void 0, layerStatus: void 0, external: [] };
|
|
5958
6424
|
if (layerArn && layerStatus) {
|
|
5959
6425
|
const status = layerStatus === "cached" ? c.dim("cached") : c.green("created");
|
|
@@ -5988,7 +6454,8 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
5988
6454
|
bucketNameMap,
|
|
5989
6455
|
mailerDomainMap,
|
|
5990
6456
|
queueNameMap,
|
|
5991
|
-
logComplete
|
|
6457
|
+
logComplete,
|
|
6458
|
+
...authSecret ? { authSecretPath: authSecret.ssmPath } : {}
|
|
5992
6459
|
};
|
|
5993
6460
|
const tableResults = [];
|
|
5994
6461
|
const appResults = [];
|
|
@@ -6191,7 +6658,8 @@ var deployCommand = Command.make(
|
|
|
6191
6658
|
stage: finalStage,
|
|
6192
6659
|
region: finalRegion,
|
|
6193
6660
|
noSites,
|
|
6194
|
-
verbose
|
|
6661
|
+
verbose,
|
|
6662
|
+
extraNodeModules: projectDir !== cwd ? [path10.join(projectDir, "node_modules")] : void 0
|
|
6195
6663
|
});
|
|
6196
6664
|
const total = results.tableResults.length + results.appResults.length + results.staticSiteResults.length + results.apiResults.length;
|
|
6197
6665
|
yield* Console3.log(`
|
|
@@ -6285,7 +6753,8 @@ Deployed ${tableResults.length} table handler(s):`));
|
|
|
6285
6753
|
project,
|
|
6286
6754
|
stage: finalStage,
|
|
6287
6755
|
region: finalRegion,
|
|
6288
|
-
exportName: foundExport
|
|
6756
|
+
exportName: foundExport,
|
|
6757
|
+
extraNodeModules: projectDir !== cwd ? [path10.join(projectDir, "node_modules")] : void 0
|
|
6289
6758
|
};
|
|
6290
6759
|
if (handlerType === "table") {
|
|
6291
6760
|
const result = yield* deployTable(input);
|
|
@@ -6390,9 +6859,9 @@ var STATUS_COLORS = {
|
|
|
6390
6859
|
var formatStatus = (status) => {
|
|
6391
6860
|
return STATUS_COLORS[status](status.padEnd(10));
|
|
6392
6861
|
};
|
|
6393
|
-
var formatRoute = (method,
|
|
6394
|
-
if (method &&
|
|
6395
|
-
if (
|
|
6862
|
+
var formatRoute = (method, path13) => {
|
|
6863
|
+
if (method && path13) return `${method.padEnd(5)} ${path13}`;
|
|
6864
|
+
if (path13) return path13;
|
|
6396
6865
|
return "";
|
|
6397
6866
|
};
|
|
6398
6867
|
var formatEntry = (entry) => {
|
|
@@ -7038,15 +7507,16 @@ var layerCommand = Command5.make(
|
|
|
7038
7507
|
"layer",
|
|
7039
7508
|
{ build: buildOption, output: outputOption, verbose: verboseOption },
|
|
7040
7509
|
({ build: build3, output, verbose }) => Effect43.gen(function* () {
|
|
7041
|
-
const { config, cwd } = yield* ProjectConfig;
|
|
7510
|
+
const { config, cwd, projectDir } = yield* ProjectConfig;
|
|
7511
|
+
const extraNodeModules = projectDir !== cwd ? [path11.join(projectDir, "node_modules")] : void 0;
|
|
7042
7512
|
if (build3) {
|
|
7043
|
-
yield* buildLayer(cwd, output, verbose);
|
|
7513
|
+
yield* buildLayer(cwd, output, verbose, extraNodeModules);
|
|
7044
7514
|
} else {
|
|
7045
|
-
yield* showLayerInfo(cwd, config?.name, verbose);
|
|
7515
|
+
yield* showLayerInfo(cwd, config?.name, verbose, extraNodeModules);
|
|
7046
7516
|
}
|
|
7047
7517
|
}).pipe(Effect43.provide(ProjectConfig.Live))
|
|
7048
7518
|
).pipe(Command5.withDescription("Inspect or locally build the shared Lambda dependency layer from package.json"));
|
|
7049
|
-
var showLayerInfo = (projectDir, projectName, verbose) => Effect43.gen(function* () {
|
|
7519
|
+
var showLayerInfo = (projectDir, projectName, verbose, extraNodeModules) => Effect43.gen(function* () {
|
|
7050
7520
|
yield* Console7.log(`
|
|
7051
7521
|
${c.bold("=== Layer Packages Preview ===")}
|
|
7052
7522
|
`);
|
|
@@ -7069,7 +7539,7 @@ ${c.bold("=== Layer Packages Preview ===")}
|
|
|
7069
7539
|
for (const dep of prodDeps) {
|
|
7070
7540
|
yield* Console7.log(` ${dep}`);
|
|
7071
7541
|
}
|
|
7072
|
-
const hash = yield* computeLockfileHash(projectDir).pipe(
|
|
7542
|
+
const hash = yield* computeLockfileHash(projectDir, extraNodeModules).pipe(
|
|
7073
7543
|
Effect43.catchAll(() => Effect43.succeed(null))
|
|
7074
7544
|
);
|
|
7075
7545
|
if (hash) {
|
|
@@ -7078,7 +7548,7 @@ Lockfile hash: ${hash}`);
|
|
|
7078
7548
|
} else {
|
|
7079
7549
|
yield* Console7.log("\nNo lockfile found (package-lock.json, pnpm-lock.yaml, or yarn.lock)");
|
|
7080
7550
|
}
|
|
7081
|
-
const { packages: allPackages, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps));
|
|
7551
|
+
const { packages: allPackages, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps, extraNodeModules));
|
|
7082
7552
|
if (layerWarnings.length > 0) {
|
|
7083
7553
|
yield* Console7.log(c.yellow(`
|
|
7084
7554
|
Warnings (${layerWarnings.length}):`));
|
|
@@ -7107,7 +7577,7 @@ Total packages for layer ${c.dim(`(${allPackages.length})`)}:`);
|
|
|
7107
7577
|
Layer name: ${projectName}-deps`);
|
|
7108
7578
|
}
|
|
7109
7579
|
});
|
|
7110
|
-
var buildLayer = (projectDir, output, verbose) => Effect43.gen(function* () {
|
|
7580
|
+
var buildLayer = (projectDir, output, verbose, extraNodeModules) => Effect43.gen(function* () {
|
|
7111
7581
|
const outputDir = path11.isAbsolute(output) ? output : path11.resolve(projectDir, output);
|
|
7112
7582
|
const layerDir = path11.join(outputDir, "nodejs", "node_modules");
|
|
7113
7583
|
const layerRoot = path11.join(outputDir, "nodejs");
|
|
@@ -7137,12 +7607,12 @@ ${c.bold("=== Building Layer Locally ===")}
|
|
|
7137
7607
|
for (const dep of prodDeps) {
|
|
7138
7608
|
yield* Console7.log(` ${dep}`);
|
|
7139
7609
|
}
|
|
7140
|
-
const hash = yield* computeLockfileHash(projectDir).pipe(
|
|
7610
|
+
const hash = yield* computeLockfileHash(projectDir, extraNodeModules).pipe(
|
|
7141
7611
|
Effect43.catchAll(() => Effect43.succeed("unknown"))
|
|
7142
7612
|
);
|
|
7143
7613
|
yield* Console7.log(`
|
|
7144
7614
|
Lockfile hash: ${hash}`);
|
|
7145
|
-
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));
|
|
7146
7616
|
if (layerWarnings.length > 0) {
|
|
7147
7617
|
yield* Console7.log(`
|
|
7148
7618
|
Warnings (${layerWarnings.length}):`);
|
|
@@ -7209,7 +7679,7 @@ var loadRequiredParams = (projectOpt, stage, region) => Effect44.gen(function* (
|
|
|
7209
7679
|
const handlers = discoverHandlers(files);
|
|
7210
7680
|
const finalStage = config?.stage ?? stage;
|
|
7211
7681
|
const finalRegion = config?.region ?? region;
|
|
7212
|
-
const params =
|
|
7682
|
+
const params = collectRequiredSecrets(handlers, project, finalStage);
|
|
7213
7683
|
return { params, project, stage: finalStage, region: finalRegion };
|
|
7214
7684
|
});
|
|
7215
7685
|
var listCommand = Command6.make(
|
|
@@ -7222,7 +7692,7 @@ var listCommand = Command6.make(
|
|
|
7222
7692
|
yield* Console8.log("No config parameters declared in handlers.");
|
|
7223
7693
|
return;
|
|
7224
7694
|
}
|
|
7225
|
-
const { existing, missing } = yield*
|
|
7695
|
+
const { existing, missing } = yield* checkMissingSecrets(params).pipe(
|
|
7226
7696
|
Effect44.provide(clients_exports.makeClients({ ssm: { region: ctx.region } }))
|
|
7227
7697
|
);
|
|
7228
7698
|
yield* Console8.log(`
|
|
@@ -7293,7 +7763,7 @@ var configRootCommand = Command6.make(
|
|
|
7293
7763
|
yield* Console8.log("No config parameters declared in handlers.");
|
|
7294
7764
|
return;
|
|
7295
7765
|
}
|
|
7296
|
-
const { missing } = yield*
|
|
7766
|
+
const { missing } = yield* checkMissingSecrets(params).pipe(
|
|
7297
7767
|
Effect44.provide(clients_exports.makeClients({ ssm: { region: ctx.region } }))
|
|
7298
7768
|
);
|
|
7299
7769
|
if (missing.length === 0) {
|
|
@@ -7342,19 +7812,262 @@ ${c.bold("Missing parameters")} ${c.dim(`(${ctx.project} / ${ctx.stage})`)}
|
|
|
7342
7812
|
);
|
|
7343
7813
|
var configCommand = configRootCommand;
|
|
7344
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
|
+
|
|
7979
|
+
// src/cli/update-check.ts
|
|
7980
|
+
import { homedir as homedir2 } from "os";
|
|
7981
|
+
import { join as join11 } from "path";
|
|
7982
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
7983
|
+
var PACKAGE_NAME = "@effortless-aws/cli";
|
|
7984
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
7985
|
+
var CACHE_DIR = join11(homedir2(), ".effortless-aws");
|
|
7986
|
+
var CACHE_FILE = join11(CACHE_DIR, "update-check.json");
|
|
7987
|
+
function readCache() {
|
|
7988
|
+
try {
|
|
7989
|
+
return JSON.parse(readFileSync6(CACHE_FILE, "utf-8"));
|
|
7990
|
+
} catch {
|
|
7991
|
+
return void 0;
|
|
7992
|
+
}
|
|
7993
|
+
}
|
|
7994
|
+
function writeCache(data) {
|
|
7995
|
+
try {
|
|
7996
|
+
mkdirSync3(CACHE_DIR, { recursive: true });
|
|
7997
|
+
writeFileSync3(CACHE_FILE, JSON.stringify(data));
|
|
7998
|
+
} catch {
|
|
7999
|
+
}
|
|
8000
|
+
}
|
|
8001
|
+
async function fetchLatestVersion() {
|
|
8002
|
+
try {
|
|
8003
|
+
const controller = new AbortController();
|
|
8004
|
+
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
8005
|
+
const res = await fetch(
|
|
8006
|
+
`https://registry.npmjs.org/${PACKAGE_NAME}/latest`,
|
|
8007
|
+
{ signal: controller.signal }
|
|
8008
|
+
);
|
|
8009
|
+
clearTimeout(timeout);
|
|
8010
|
+
if (!res.ok) return void 0;
|
|
8011
|
+
const data = await res.json();
|
|
8012
|
+
return data.version;
|
|
8013
|
+
} catch {
|
|
8014
|
+
return void 0;
|
|
8015
|
+
}
|
|
8016
|
+
}
|
|
8017
|
+
function compareVersions(current, latest) {
|
|
8018
|
+
const parse = (v) => v.split(".").map(Number);
|
|
8019
|
+
const [cMajor, cMinor, cPatch] = parse(current);
|
|
8020
|
+
const [lMajor, lMinor, lPatch] = parse(latest);
|
|
8021
|
+
if (lMajor !== cMajor) return lMajor > cMajor;
|
|
8022
|
+
if (lMinor !== cMinor) return lMinor > cMinor;
|
|
8023
|
+
return lPatch > cPatch;
|
|
8024
|
+
}
|
|
8025
|
+
async function checkForUpdate(currentVersion) {
|
|
8026
|
+
const cache = readCache();
|
|
8027
|
+
const now = Date.now();
|
|
8028
|
+
let latestVersion;
|
|
8029
|
+
if (cache && now - cache.lastCheck < CHECK_INTERVAL_MS) {
|
|
8030
|
+
latestVersion = cache.latestVersion;
|
|
8031
|
+
} else {
|
|
8032
|
+
latestVersion = await fetchLatestVersion();
|
|
8033
|
+
if (latestVersion) {
|
|
8034
|
+
writeCache({ lastCheck: now, latestVersion });
|
|
8035
|
+
}
|
|
8036
|
+
}
|
|
8037
|
+
if (latestVersion && compareVersions(currentVersion, latestVersion)) {
|
|
8038
|
+
const border = "\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510";
|
|
8039
|
+
const bottom = "\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518";
|
|
8040
|
+
const pad = (line, width) => {
|
|
8041
|
+
const visible = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
8042
|
+
return line + " ".repeat(Math.max(0, width - visible.length));
|
|
8043
|
+
};
|
|
8044
|
+
const W = 39;
|
|
8045
|
+
const line1 = `Update available! ${c.dim(currentVersion)} ${c.dim("\u2192")} ${c.green(latestVersion)}`;
|
|
8046
|
+
const line2 = `Run ${c.cyan(`pnpm i -g ${PACKAGE_NAME}`)} to update`;
|
|
8047
|
+
console.log();
|
|
8048
|
+
console.log(` ${border}`);
|
|
8049
|
+
console.log(` \u2502 ${pad(line1, W)} \u2502`);
|
|
8050
|
+
console.log(` \u2502 ${pad(line2, W)} \u2502`);
|
|
8051
|
+
console.log(` ${bottom}`);
|
|
8052
|
+
console.log();
|
|
8053
|
+
}
|
|
8054
|
+
}
|
|
8055
|
+
|
|
7345
8056
|
// src/cli/index.ts
|
|
7346
8057
|
var require2 = createRequire2(import.meta.url);
|
|
7347
8058
|
var { version } = require2("../../package.json");
|
|
7348
|
-
var mainCommand =
|
|
7349
|
-
|
|
7350
|
-
|
|
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")
|
|
7351
8062
|
);
|
|
7352
|
-
var cli =
|
|
8063
|
+
var cli = Command8.run(mainCommand, {
|
|
7353
8064
|
name: "effortless",
|
|
7354
8065
|
version
|
|
7355
8066
|
});
|
|
8067
|
+
var updateCheck = checkForUpdate(version);
|
|
7356
8068
|
cli(process.argv).pipe(
|
|
7357
|
-
|
|
7358
|
-
|
|
8069
|
+
Effect46.provide(NodeContext.layer),
|
|
8070
|
+
Effect46.provide(CliConfig.layer({ showBuiltIns: false, showTypes: false })),
|
|
8071
|
+
Effect46.tap(() => Effect46.promise(() => updateCheck)),
|
|
7359
8072
|
NodeRuntime.runMain
|
|
7360
8073
|
);
|