@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.
- package/dist/cli/index.js +818 -255
- 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,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
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
const
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
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
|
|
4244
|
-
const defineFn = handlerRegistry.staticSite.defineFn;
|
|
4372
|
+
const calls = findDefineCalls(sourceFile, handlerRegistry.staticSite.defineFn);
|
|
4245
4373
|
let middlewareFnText;
|
|
4246
4374
|
let exportName;
|
|
4247
|
-
const
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
|
4501
|
-
|
|
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 <
|
|
4504
|
-
const batch =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
5157
|
-
|
|
5158
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
5746
|
-
|
|
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
|
|
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,
|
|
6133
|
+
var resolveHandlerEnv = (depsKeys, secretEntries, ctx) => {
|
|
5768
6134
|
const resolved = mergeResolved(
|
|
5769
6135
|
resolveDeps(depsKeys, ctx.tableNameMap, ctx.bucketNameMap, ctx.mailerDomainMap, ctx.queueNameMap),
|
|
5770
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
5943
|
-
|
|
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
|
|
5998
|
-
|
|
5999
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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,
|
|
6468
|
-
if (method &&
|
|
6469
|
-
if (
|
|
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 =
|
|
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*
|
|
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*
|
|
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
|
|
7422
|
-
import { readFileSync as
|
|
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 =
|
|
7426
|
-
var CACHE_FILE =
|
|
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(
|
|
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
|
-
|
|
7437
|
-
|
|
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 =
|
|
7500
|
-
|
|
7501
|
-
|
|
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 =
|
|
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
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
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
|
);
|