@effortless-aws/cli 0.1.0 → 0.2.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/README.md +49 -0
- package/dist/cli/index.js +460 -300
- package/package.json +2 -2
- package/dist/cli/index.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -6,15 +6,15 @@ var __export = (target, all) => {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
// src/cli/index.ts
|
|
9
|
-
import { Command as Command7 } from "@effect/cli";
|
|
9
|
+
import { CliConfig, Command as Command7 } from "@effect/cli";
|
|
10
10
|
import { NodeContext, NodeRuntime } from "@effect/platform-node";
|
|
11
|
-
import { Effect as
|
|
11
|
+
import { Effect as Effect45 } from "effect";
|
|
12
12
|
import { createRequire as createRequire2 } from "module";
|
|
13
13
|
|
|
14
14
|
// src/cli/commands/deploy.ts
|
|
15
15
|
import { Args, Command } from "@effect/cli";
|
|
16
|
-
import { Effect as
|
|
17
|
-
import * as
|
|
16
|
+
import { Effect as Effect38, Console as Console3, Logger, LogLevel, Option } from "effect";
|
|
17
|
+
import * as path9 from "path";
|
|
18
18
|
|
|
19
19
|
// src/deploy/deploy.ts
|
|
20
20
|
import { Effect as Effect35, Console as Console2 } from "effect";
|
|
@@ -1955,19 +1955,47 @@ var ensureFunctionUrl = (functionName) => Effect13.gen(function* () {
|
|
|
1955
1955
|
return { functionUrl: result.FunctionUrl };
|
|
1956
1956
|
});
|
|
1957
1957
|
var addCloudFrontPermission = (functionName, distributionArn) => Effect13.gen(function* () {
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
)
|
|
1958
|
+
const statements = [
|
|
1959
|
+
{
|
|
1960
|
+
statementId: "cloudfront-oac",
|
|
1961
|
+
action: "lambda:InvokeFunctionUrl",
|
|
1962
|
+
authType: "AWS_IAM"
|
|
1963
|
+
},
|
|
1964
|
+
{
|
|
1965
|
+
statementId: "cloudfront-oac-invoke",
|
|
1966
|
+
action: "lambda:InvokeFunction",
|
|
1967
|
+
authType: void 0
|
|
1968
|
+
}
|
|
1969
|
+
];
|
|
1970
|
+
for (const stmt of statements) {
|
|
1971
|
+
yield* lambda_exports.make("add_permission", {
|
|
1972
|
+
FunctionName: functionName,
|
|
1973
|
+
StatementId: stmt.statementId,
|
|
1974
|
+
Action: stmt.action,
|
|
1975
|
+
Principal: "cloudfront.amazonaws.com",
|
|
1976
|
+
SourceArn: distributionArn,
|
|
1977
|
+
...stmt.authType ? { FunctionUrlAuthType: stmt.authType } : {}
|
|
1978
|
+
}).pipe(
|
|
1979
|
+
Effect13.catchIf(
|
|
1980
|
+
(e) => e._tag === "LambdaError" && e.is("ResourceConflictException"),
|
|
1981
|
+
() => Effect13.gen(function* () {
|
|
1982
|
+
yield* Effect13.logDebug(`Permission ${stmt.statementId} exists for ${functionName}, replacing...`);
|
|
1983
|
+
yield* lambda_exports.make("remove_permission", {
|
|
1984
|
+
FunctionName: functionName,
|
|
1985
|
+
StatementId: stmt.statementId
|
|
1986
|
+
});
|
|
1987
|
+
yield* lambda_exports.make("add_permission", {
|
|
1988
|
+
FunctionName: functionName,
|
|
1989
|
+
StatementId: stmt.statementId,
|
|
1990
|
+
Action: stmt.action,
|
|
1991
|
+
Principal: "cloudfront.amazonaws.com",
|
|
1992
|
+
SourceArn: distributionArn,
|
|
1993
|
+
...stmt.authType ? { FunctionUrlAuthType: stmt.authType } : {}
|
|
1994
|
+
});
|
|
1995
|
+
})
|
|
1996
|
+
)
|
|
1997
|
+
);
|
|
1998
|
+
}
|
|
1971
1999
|
});
|
|
1972
2000
|
var deleteLambda = (functionName) => Effect13.gen(function* () {
|
|
1973
2001
|
yield* Effect13.logDebug(`Deleting Lambda function: ${functionName}`);
|
|
@@ -2600,6 +2628,64 @@ var readProductionDependencies = (projectDir) => Effect18.gen(function* () {
|
|
|
2600
2628
|
const pkg = JSON.parse(content);
|
|
2601
2629
|
return Object.keys(pkg.dependencies ?? {});
|
|
2602
2630
|
});
|
|
2631
|
+
var DEV_ONLY_PACKAGES = /* @__PURE__ */ new Set([
|
|
2632
|
+
"typescript",
|
|
2633
|
+
"ts-node",
|
|
2634
|
+
"tsx",
|
|
2635
|
+
"vitest",
|
|
2636
|
+
"jest",
|
|
2637
|
+
"mocha",
|
|
2638
|
+
"eslint",
|
|
2639
|
+
"prettier",
|
|
2640
|
+
"tsup",
|
|
2641
|
+
"esbuild",
|
|
2642
|
+
"webpack",
|
|
2643
|
+
"rollup",
|
|
2644
|
+
"vite",
|
|
2645
|
+
"turbo",
|
|
2646
|
+
"husky",
|
|
2647
|
+
"lint-staged",
|
|
2648
|
+
"commitlint",
|
|
2649
|
+
"nodemon",
|
|
2650
|
+
"ts-jest",
|
|
2651
|
+
"concurrently",
|
|
2652
|
+
"rimraf"
|
|
2653
|
+
]);
|
|
2654
|
+
var DEV_ONLY_PREFIXES = [
|
|
2655
|
+
"@types/",
|
|
2656
|
+
"@typescript-eslint/",
|
|
2657
|
+
"@eslint/",
|
|
2658
|
+
"eslint-plugin-",
|
|
2659
|
+
"eslint-config-",
|
|
2660
|
+
"@vitest/",
|
|
2661
|
+
"@jest/"
|
|
2662
|
+
];
|
|
2663
|
+
var checkDependencyWarnings = (projectDir) => Effect18.gen(function* () {
|
|
2664
|
+
const pkgPath = path.join(projectDir, "package.json");
|
|
2665
|
+
const content = yield* Effect18.tryPromise({
|
|
2666
|
+
try: () => fs.readFile(pkgPath, "utf-8"),
|
|
2667
|
+
catch: () => Effect18.succeed(null)
|
|
2668
|
+
});
|
|
2669
|
+
if (!content) return [];
|
|
2670
|
+
const pkg = JSON.parse(content);
|
|
2671
|
+
const deps = Object.keys(pkg.dependencies ?? {});
|
|
2672
|
+
const devDeps = Object.keys(pkg.devDependencies ?? {});
|
|
2673
|
+
const warnings = [];
|
|
2674
|
+
const devInProd = deps.filter(
|
|
2675
|
+
(d) => DEV_ONLY_PACKAGES.has(d) || DEV_ONLY_PREFIXES.some((p) => d.startsWith(p))
|
|
2676
|
+
);
|
|
2677
|
+
if (devInProd.length > 0) {
|
|
2678
|
+
warnings.push(
|
|
2679
|
+
`These packages are in "dependencies" but look like dev tools (they will bloat the Lambda layer): ${devInProd.join(", ")}. Consider moving them to "devDependencies".`
|
|
2680
|
+
);
|
|
2681
|
+
}
|
|
2682
|
+
if (deps.length === 0 && devDeps.length > 0) {
|
|
2683
|
+
warnings.push(
|
|
2684
|
+
`"dependencies" is empty but "devDependencies" has ${devDeps.length} package(s). Runtime packages must be in "dependencies" to be included in the Lambda layer.`
|
|
2685
|
+
);
|
|
2686
|
+
}
|
|
2687
|
+
return warnings;
|
|
2688
|
+
});
|
|
2603
2689
|
var getPackageRealPath = (projectDir, pkgName) => {
|
|
2604
2690
|
const pkgPath = path.join(projectDir, "node_modules", pkgName);
|
|
2605
2691
|
if (!fsSync.existsSync(pkgPath)) return null;
|
|
@@ -2788,6 +2874,12 @@ var getExistingLayerByHash = (layerName, expectedHash) => Effect18.gen(function*
|
|
|
2788
2874
|
};
|
|
2789
2875
|
});
|
|
2790
2876
|
var ensureLayer = (config) => Effect18.gen(function* () {
|
|
2877
|
+
const depWarnings = yield* checkDependencyWarnings(config.projectDir).pipe(
|
|
2878
|
+
Effect18.catchAll(() => Effect18.succeed([]))
|
|
2879
|
+
);
|
|
2880
|
+
for (const w of depWarnings) {
|
|
2881
|
+
yield* Effect18.logWarning(`[layer] ${w}`);
|
|
2882
|
+
}
|
|
2791
2883
|
const dependencies = yield* readProductionDependencies(config.projectDir).pipe(
|
|
2792
2884
|
Effect18.catchAll(() => Effect18.succeed([]))
|
|
2793
2885
|
);
|
|
@@ -3496,7 +3588,7 @@ var ensureDistribution = (input) => Effect21.gen(function* () {
|
|
|
3496
3588
|
};
|
|
3497
3589
|
});
|
|
3498
3590
|
var ensureSsrDistribution = (input) => Effect21.gen(function* () {
|
|
3499
|
-
const { project, stage, handlerName, bucketName, bucketRegion, s3OacId, lambdaOriginDomain, lambdaOacId, assetPatterns, tags, aliases, acmCertificateArn } = input;
|
|
3591
|
+
const { project, stage, handlerName, bucketName, bucketRegion, s3OacId, lambdaOriginDomain, lambdaOacId, assetPatterns, tags, aliases, acmCertificateArn, apiOriginDomain, routePatterns } = input;
|
|
3500
3592
|
const comment = makeDistComment(project, stage, handlerName);
|
|
3501
3593
|
const lambdaOriginId = `Lambda-${project}-${stage}-${handlerName}`;
|
|
3502
3594
|
const s3OriginId = `S3-${bucketName}`;
|
|
@@ -3509,6 +3601,8 @@ var ensureSsrDistribution = (input) => Effect21.gen(function* () {
|
|
|
3509
3601
|
} : void 0;
|
|
3510
3602
|
const ALL_METHODS = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"];
|
|
3511
3603
|
const CACHED_METHODS = ["GET", "HEAD"];
|
|
3604
|
+
const hasApiRoutes = apiOriginDomain && routePatterns && routePatterns.length > 0;
|
|
3605
|
+
const apiOriginId = hasApiRoutes ? `API-${project}-${stage}` : void 0;
|
|
3512
3606
|
const originsItems = [
|
|
3513
3607
|
{
|
|
3514
3608
|
Id: lambdaOriginId,
|
|
@@ -3532,7 +3626,23 @@ var ensureSsrDistribution = (input) => Effect21.gen(function* () {
|
|
|
3532
3626
|
OriginAccessControlId: s3OacId,
|
|
3533
3627
|
S3OriginConfig: { OriginAccessIdentity: "" },
|
|
3534
3628
|
CustomHeaders: { Quantity: 0, Items: [] }
|
|
3535
|
-
}
|
|
3629
|
+
},
|
|
3630
|
+
...hasApiRoutes ? [{
|
|
3631
|
+
Id: apiOriginId,
|
|
3632
|
+
DomainName: apiOriginDomain,
|
|
3633
|
+
OriginPath: "",
|
|
3634
|
+
ConnectionAttempts: 3,
|
|
3635
|
+
ConnectionTimeout: 10,
|
|
3636
|
+
CustomOriginConfig: {
|
|
3637
|
+
HTTPPort: 80,
|
|
3638
|
+
HTTPSPort: 443,
|
|
3639
|
+
OriginProtocolPolicy: "https-only",
|
|
3640
|
+
OriginSslProtocols: { Quantity: 1, Items: ["TLSv1.2"] },
|
|
3641
|
+
OriginReadTimeout: 30,
|
|
3642
|
+
OriginKeepaliveTimeout: 5
|
|
3643
|
+
},
|
|
3644
|
+
CustomHeaders: { Quantity: 0, Items: [] }
|
|
3645
|
+
}] : []
|
|
3536
3646
|
];
|
|
3537
3647
|
const defaultCacheBehavior = {
|
|
3538
3648
|
TargetOriginId: lambdaOriginId,
|
|
@@ -3543,32 +3653,50 @@ var ensureSsrDistribution = (input) => Effect21.gen(function* () {
|
|
|
3543
3653
|
CachedMethods: { Quantity: 2, Items: [...CACHED_METHODS] }
|
|
3544
3654
|
},
|
|
3545
3655
|
Compress: true,
|
|
3656
|
+
SmoothStreaming: false,
|
|
3546
3657
|
CachePolicyId: CACHING_DISABLED_POLICY_ID,
|
|
3547
3658
|
OriginRequestPolicyId: ALL_VIEWER_EXCEPT_HOST_HEADER_POLICY_ID,
|
|
3548
3659
|
ResponseHeadersPolicyId: SECURITY_HEADERS_POLICY_ID,
|
|
3549
3660
|
FunctionAssociations: { Quantity: 0, Items: [] },
|
|
3550
|
-
LambdaFunctionAssociations: { Quantity: 0, Items: [] }
|
|
3661
|
+
LambdaFunctionAssociations: { Quantity: 0, Items: [] },
|
|
3662
|
+
FieldLevelEncryptionId: ""
|
|
3551
3663
|
};
|
|
3552
|
-
const
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3664
|
+
const apiRouteBehaviors = hasApiRoutes ? routePatterns.map((pattern) => ({
|
|
3665
|
+
PathPattern: pattern,
|
|
3666
|
+
TargetOriginId: apiOriginId,
|
|
3667
|
+
ViewerProtocolPolicy: "redirect-to-https",
|
|
3668
|
+
AllowedMethods: {
|
|
3669
|
+
Quantity: 7,
|
|
3670
|
+
Items: [...ALL_METHODS],
|
|
3671
|
+
CachedMethods: { Quantity: 2, Items: [...CACHED_METHODS] }
|
|
3672
|
+
},
|
|
3673
|
+
Compress: true,
|
|
3674
|
+
SmoothStreaming: false,
|
|
3675
|
+
CachePolicyId: CACHING_DISABLED_POLICY_ID,
|
|
3676
|
+
OriginRequestPolicyId: ALL_VIEWER_EXCEPT_HOST_HEADER_POLICY_ID,
|
|
3677
|
+
FunctionAssociations: { Quantity: 0, Items: [] },
|
|
3678
|
+
LambdaFunctionAssociations: { Quantity: 0, Items: [] },
|
|
3679
|
+
FieldLevelEncryptionId: ""
|
|
3680
|
+
})) : [];
|
|
3681
|
+
const assetBehaviors = assetPatterns.map((pattern) => ({
|
|
3682
|
+
PathPattern: pattern,
|
|
3683
|
+
TargetOriginId: s3OriginId,
|
|
3684
|
+
ViewerProtocolPolicy: "redirect-to-https",
|
|
3685
|
+
AllowedMethods: {
|
|
3686
|
+
Quantity: 2,
|
|
3687
|
+
Items: [...CACHED_METHODS],
|
|
3688
|
+
CachedMethods: { Quantity: 2, Items: [...CACHED_METHODS] }
|
|
3689
|
+
},
|
|
3690
|
+
Compress: true,
|
|
3691
|
+
SmoothStreaming: false,
|
|
3692
|
+
CachePolicyId: CACHING_OPTIMIZED_POLICY_ID,
|
|
3693
|
+
ResponseHeadersPolicyId: SECURITY_HEADERS_POLICY_ID,
|
|
3694
|
+
FunctionAssociations: { Quantity: 0, Items: [] },
|
|
3695
|
+
LambdaFunctionAssociations: { Quantity: 0, Items: [] },
|
|
3696
|
+
FieldLevelEncryptionId: ""
|
|
3697
|
+
}));
|
|
3698
|
+
const allBehaviors = [...apiRouteBehaviors, ...assetBehaviors];
|
|
3699
|
+
const cacheBehaviors = allBehaviors.length > 0 ? { Quantity: allBehaviors.length, Items: allBehaviors } : { Quantity: 0, Items: [] };
|
|
3572
3700
|
const existing = yield* findDistributionByTags(project, stage, handlerName);
|
|
3573
3701
|
if (existing) {
|
|
3574
3702
|
const configResult = yield* cloudfront_exports.make("get_distribution_config", {
|
|
@@ -4345,6 +4473,19 @@ var discoverHandlers = (files) => {
|
|
|
4345
4473
|
}
|
|
4346
4474
|
return { httpHandlers, tableHandlers, appHandlers, staticSiteHandlers, fifoQueueHandlers, bucketHandlers, mailerHandlers, apiHandlers };
|
|
4347
4475
|
};
|
|
4476
|
+
var flattenHandlers = (discovered) => {
|
|
4477
|
+
const entries = (type, handlers) => handlers.flatMap((h) => h.exports.map((e) => ({ exportName: e.exportName, file: h.file, type })));
|
|
4478
|
+
return [
|
|
4479
|
+
...entries("http", discovered.httpHandlers),
|
|
4480
|
+
...entries("table", discovered.tableHandlers),
|
|
4481
|
+
...entries("app", discovered.appHandlers),
|
|
4482
|
+
...entries("site", discovered.staticSiteHandlers),
|
|
4483
|
+
...entries("queue", discovered.fifoQueueHandlers),
|
|
4484
|
+
...entries("bucket", discovered.bucketHandlers),
|
|
4485
|
+
...entries("mailer", discovered.mailerHandlers),
|
|
4486
|
+
...entries("api", discovered.apiHandlers)
|
|
4487
|
+
];
|
|
4488
|
+
};
|
|
4348
4489
|
|
|
4349
4490
|
// src/deploy/resolve-config.ts
|
|
4350
4491
|
import { Effect as Effect25 } from "effect";
|
|
@@ -4418,10 +4559,10 @@ var ensureLayerAndExternal = (input) => Effect26.gen(function* () {
|
|
|
4418
4559
|
project: input.project,
|
|
4419
4560
|
stage: input.stage,
|
|
4420
4561
|
region: input.region,
|
|
4421
|
-
projectDir: input.
|
|
4562
|
+
projectDir: input.packageDir
|
|
4422
4563
|
});
|
|
4423
|
-
const prodDeps = layerResult ? yield* readProductionDependencies(input.
|
|
4424
|
-
const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect26.sync(() => collectLayerPackages(input.
|
|
4564
|
+
const prodDeps = layerResult ? yield* readProductionDependencies(input.packageDir) : [];
|
|
4565
|
+
const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect26.sync(() => collectLayerPackages(input.packageDir, prodDeps)) : { packages: [], warnings: [] };
|
|
4425
4566
|
for (const warning of layerWarnings) {
|
|
4426
4567
|
yield* Effect26.logWarning(`[layer] ${warning}`);
|
|
4427
4568
|
}
|
|
@@ -4536,7 +4677,7 @@ var deploy = (input) => Effect27.gen(function* () {
|
|
|
4536
4677
|
project: input.project,
|
|
4537
4678
|
stage: tagCtx.stage,
|
|
4538
4679
|
region: input.region,
|
|
4539
|
-
|
|
4680
|
+
packageDir: input.packageDir ?? input.projectDir
|
|
4540
4681
|
});
|
|
4541
4682
|
const { functionArn } = yield* deployLambda({
|
|
4542
4683
|
input,
|
|
@@ -4589,7 +4730,7 @@ var deployAll = (input) => Effect27.gen(function* () {
|
|
|
4589
4730
|
project: input.project,
|
|
4590
4731
|
stage: tagCtx.stage,
|
|
4591
4732
|
region: input.region,
|
|
4592
|
-
|
|
4733
|
+
packageDir: input.packageDir ?? input.projectDir
|
|
4593
4734
|
});
|
|
4594
4735
|
yield* Effect27.logDebug("Setting up API Gateway...");
|
|
4595
4736
|
const { apiId } = yield* ensureProjectApi({
|
|
@@ -4701,7 +4842,7 @@ var deployTable = (input) => Effect28.gen(function* () {
|
|
|
4701
4842
|
project: input.project,
|
|
4702
4843
|
stage: resolveStage(input.stage),
|
|
4703
4844
|
region: input.region,
|
|
4704
|
-
|
|
4845
|
+
packageDir: input.packageDir ?? input.projectDir
|
|
4705
4846
|
});
|
|
4706
4847
|
const result = yield* deployTableFunction({
|
|
4707
4848
|
input,
|
|
@@ -4730,7 +4871,7 @@ var deployAllTables = (input) => Effect28.gen(function* () {
|
|
|
4730
4871
|
project: input.project,
|
|
4731
4872
|
stage: resolveStage(input.stage),
|
|
4732
4873
|
region: input.region,
|
|
4733
|
-
|
|
4874
|
+
packageDir: input.packageDir ?? input.projectDir
|
|
4734
4875
|
});
|
|
4735
4876
|
const results = yield* Effect28.forEach(
|
|
4736
4877
|
functions,
|
|
@@ -4763,12 +4904,31 @@ var deployApp = (input) => Effect29.gen(function* () {
|
|
|
4763
4904
|
const stage = resolveStage(input.stage);
|
|
4764
4905
|
const handlerName = exportName;
|
|
4765
4906
|
const tagCtx = { project, stage, handler: handlerName };
|
|
4907
|
+
const routePatterns = fn13.routePatterns;
|
|
4908
|
+
if (routePatterns.length > 0 && !input.apiOriginDomain) {
|
|
4909
|
+
return yield* Effect29.fail(
|
|
4910
|
+
new Error(
|
|
4911
|
+
`App "${exportName}" has routes but no API Gateway exists. Ensure defineHttp() or defineApi() handlers are included in the discovery patterns.`
|
|
4912
|
+
)
|
|
4913
|
+
);
|
|
4914
|
+
}
|
|
4766
4915
|
if (config.build) {
|
|
4767
4916
|
yield* Effect29.logDebug(`Building app: ${config.build}`);
|
|
4917
|
+
const buildStart = Date.now();
|
|
4768
4918
|
yield* Effect29.try({
|
|
4769
|
-
try: () => execSync(config.build, {
|
|
4770
|
-
|
|
4919
|
+
try: () => execSync(config.build, {
|
|
4920
|
+
cwd: projectDir,
|
|
4921
|
+
stdio: input.verbose ? "inherit" : "pipe"
|
|
4922
|
+
}),
|
|
4923
|
+
catch: (error) => {
|
|
4924
|
+
if (!input.verbose && error && typeof error === "object" && "stderr" in error) {
|
|
4925
|
+
const stderr = String(error.stderr);
|
|
4926
|
+
if (stderr) process.stderr.write(stderr);
|
|
4927
|
+
}
|
|
4928
|
+
return new Error(`App build failed: ${config.build}`);
|
|
4929
|
+
}
|
|
4771
4930
|
});
|
|
4931
|
+
yield* Effect29.logDebug(`App built in ${((Date.now() - buildStart) / 1e3).toFixed(1)}s`);
|
|
4772
4932
|
}
|
|
4773
4933
|
const serverDir = path5.resolve(projectDir, config.server);
|
|
4774
4934
|
yield* Effect29.logDebug(`Zipping server directory: ${serverDir}`);
|
|
@@ -4834,7 +4994,8 @@ var deployApp = (input) => Effect29.gen(function* () {
|
|
|
4834
4994
|
assetPatterns,
|
|
4835
4995
|
tags: makeTags(tagCtx, "cloudfront-distribution"),
|
|
4836
4996
|
aliases,
|
|
4837
|
-
acmCertificateArn
|
|
4997
|
+
acmCertificateArn,
|
|
4998
|
+
...input.apiOriginDomain && routePatterns.length > 0 ? { apiOriginDomain: input.apiOriginDomain, routePatterns } : {}
|
|
4838
4999
|
});
|
|
4839
5000
|
yield* addCloudFrontPermission(lambdaName, distributionArn);
|
|
4840
5001
|
yield* putBucketPolicyForOAC(bucketName, distributionArn);
|
|
@@ -4948,10 +5109,21 @@ var deployStaticSite = (input) => Effect30.gen(function* () {
|
|
|
4948
5109
|
}
|
|
4949
5110
|
if (config.build) {
|
|
4950
5111
|
yield* Effect30.logDebug(`Building site: ${config.build}`);
|
|
5112
|
+
const buildStart = Date.now();
|
|
4951
5113
|
yield* Effect30.try({
|
|
4952
|
-
try: () => execSync2(config.build, {
|
|
4953
|
-
|
|
5114
|
+
try: () => execSync2(config.build, {
|
|
5115
|
+
cwd: projectDir,
|
|
5116
|
+
stdio: input.verbose ? "inherit" : "pipe"
|
|
5117
|
+
}),
|
|
5118
|
+
catch: (error) => {
|
|
5119
|
+
if (!input.verbose && error && typeof error === "object" && "stderr" in error) {
|
|
5120
|
+
const stderr = String(error.stderr);
|
|
5121
|
+
if (stderr) process.stderr.write(stderr);
|
|
5122
|
+
}
|
|
5123
|
+
return new Error(`Site build failed: ${config.build}`);
|
|
5124
|
+
}
|
|
4954
5125
|
});
|
|
5126
|
+
yield* Effect30.logDebug(`Site built in ${((Date.now() - buildStart) / 1e3).toFixed(1)}s`);
|
|
4955
5127
|
}
|
|
4956
5128
|
const bucketName = `${project}-${stage}-${handlerName}-site`.toLowerCase();
|
|
4957
5129
|
yield* ensureBucket({
|
|
@@ -5295,7 +5467,7 @@ var prepareLayer = (input) => Effect35.gen(function* () {
|
|
|
5295
5467
|
project: input.project,
|
|
5296
5468
|
stage: input.stage,
|
|
5297
5469
|
region: input.region,
|
|
5298
|
-
projectDir: input.
|
|
5470
|
+
projectDir: input.packageDir
|
|
5299
5471
|
}).pipe(
|
|
5300
5472
|
Effect35.provide(
|
|
5301
5473
|
clients_exports.makeClients({
|
|
@@ -5303,8 +5475,8 @@ var prepareLayer = (input) => Effect35.gen(function* () {
|
|
|
5303
5475
|
})
|
|
5304
5476
|
)
|
|
5305
5477
|
);
|
|
5306
|
-
const prodDeps = layerResult ? yield* readProductionDependencies(input.
|
|
5307
|
-
const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect35.sync(() => collectLayerPackages(input.
|
|
5478
|
+
const prodDeps = layerResult ? yield* readProductionDependencies(input.packageDir) : [];
|
|
5479
|
+
const { packages: external, warnings: layerWarnings } = prodDeps.length > 0 ? yield* Effect35.sync(() => collectLayerPackages(input.packageDir, prodDeps)) : { packages: [], warnings: [] };
|
|
5308
5480
|
for (const warning of layerWarnings) {
|
|
5309
5481
|
yield* Effect35.logWarning(`[layer] ${warning}`);
|
|
5310
5482
|
}
|
|
@@ -5493,9 +5665,10 @@ var buildTableTasks = (ctx, handlers, results) => {
|
|
|
5493
5665
|
}
|
|
5494
5666
|
return tasks;
|
|
5495
5667
|
};
|
|
5496
|
-
var buildAppTasks = (ctx, handlers, results) => {
|
|
5668
|
+
var buildAppTasks = (ctx, handlers, results, apiId) => {
|
|
5497
5669
|
const tasks = [];
|
|
5498
5670
|
const { region } = ctx.input;
|
|
5671
|
+
const apiOriginDomain = apiId ? `${apiId}.execute-api.${region}.amazonaws.com` : void 0;
|
|
5499
5672
|
for (const { exports } of handlers) {
|
|
5500
5673
|
for (const fn13 of exports) {
|
|
5501
5674
|
tasks.push(
|
|
@@ -5505,7 +5678,9 @@ var buildAppTasks = (ctx, handlers, results) => {
|
|
|
5505
5678
|
project: ctx.input.project,
|
|
5506
5679
|
stage: ctx.input.stage,
|
|
5507
5680
|
region,
|
|
5508
|
-
fn: fn13
|
|
5681
|
+
fn: fn13,
|
|
5682
|
+
verbose: ctx.input.verbose,
|
|
5683
|
+
...apiOriginDomain ? { apiOriginDomain } : {}
|
|
5509
5684
|
}).pipe(Effect35.provide(clients_exports.makeClients({
|
|
5510
5685
|
lambda: { region },
|
|
5511
5686
|
iam: { region },
|
|
@@ -5536,6 +5711,7 @@ var buildStaticSiteTasks = (ctx, handlers, results, apiId) => {
|
|
|
5536
5711
|
stage: ctx.input.stage,
|
|
5537
5712
|
region,
|
|
5538
5713
|
fn: fn13,
|
|
5714
|
+
verbose: ctx.input.verbose,
|
|
5539
5715
|
...fn13.hasHandler ? { file } : {},
|
|
5540
5716
|
...apiOriginDomain ? { apiOriginDomain } : {}
|
|
5541
5717
|
}).pipe(Effect35.provide(clients_exports.makeClients({
|
|
@@ -5718,7 +5894,7 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
5718
5894
|
project: input.project,
|
|
5719
5895
|
stage,
|
|
5720
5896
|
region: input.region,
|
|
5721
|
-
|
|
5897
|
+
packageDir: input.packageDir ?? input.projectDir
|
|
5722
5898
|
});
|
|
5723
5899
|
if (layerArn && layerStatus) {
|
|
5724
5900
|
const status = layerStatus === "cached" ? c.dim("cached") : c.green("created");
|
|
@@ -5729,7 +5905,10 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
5729
5905
|
const staticSitesNeedApi = !input.noSites && staticSiteHandlers.some(
|
|
5730
5906
|
({ exports }) => exports.some((fn13) => fn13.routePatterns.length > 0)
|
|
5731
5907
|
);
|
|
5732
|
-
|
|
5908
|
+
const appsNeedApi = appHandlers.some(
|
|
5909
|
+
({ exports }) => exports.some((fn13) => fn13.routePatterns.length > 0)
|
|
5910
|
+
);
|
|
5911
|
+
if (totalHttpHandlers > 0 || totalApiHandlers > 0 || staticSitesNeedApi || appsNeedApi) {
|
|
5733
5912
|
const tagCtx = {
|
|
5734
5913
|
project: input.project,
|
|
5735
5914
|
stage,
|
|
@@ -5794,7 +5973,7 @@ var deployProject = (input) => Effect35.gen(function* () {
|
|
|
5794
5973
|
const tasks = [
|
|
5795
5974
|
...apiId ? buildHttpTasks(ctx, httpHandlers, apiId, httpResults) : [],
|
|
5796
5975
|
...buildTableTasks(ctx, tableHandlers, tableResults),
|
|
5797
|
-
...buildAppTasks(ctx, appHandlers, appResults),
|
|
5976
|
+
...buildAppTasks(ctx, appHandlers, appResults, apiId),
|
|
5798
5977
|
...input.noSites ? [] : buildStaticSiteTasks(ctx, staticSiteHandlers, staticSiteResults, apiId),
|
|
5799
5978
|
...buildFifoQueueTasks(ctx, fifoQueueHandlers, fifoQueueResults),
|
|
5800
5979
|
...buildBucketTasks(ctx, bucketHandlers, bucketResults),
|
|
@@ -5846,18 +6025,22 @@ import * as path7 from "path";
|
|
|
5846
6025
|
import * as fs4 from "fs";
|
|
5847
6026
|
import { pathToFileURL } from "url";
|
|
5848
6027
|
import * as esbuild2 from "esbuild";
|
|
5849
|
-
|
|
6028
|
+
import { Effect as Effect36 } from "effect";
|
|
6029
|
+
var loadConfig = Effect36.fn("loadConfig")(function* () {
|
|
5850
6030
|
const configPath = path7.resolve(process.cwd(), "effortless.config.ts");
|
|
5851
6031
|
if (!fs4.existsSync(configPath)) {
|
|
5852
6032
|
return null;
|
|
5853
6033
|
}
|
|
5854
|
-
const result =
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
6034
|
+
const result = yield* Effect36.tryPromise({
|
|
6035
|
+
try: () => esbuild2.build({
|
|
6036
|
+
entryPoints: [configPath],
|
|
6037
|
+
bundle: true,
|
|
6038
|
+
write: false,
|
|
6039
|
+
format: "esm",
|
|
6040
|
+
platform: "node",
|
|
6041
|
+
external: ["effortless-aws"]
|
|
6042
|
+
}),
|
|
6043
|
+
catch: (error) => new Error(`Failed to compile config: ${error}`)
|
|
5861
6044
|
});
|
|
5862
6045
|
const output = result.outputFiles?.[0];
|
|
5863
6046
|
if (!output) {
|
|
@@ -5866,13 +6049,12 @@ var loadConfig = async () => {
|
|
|
5866
6049
|
const code = output.text;
|
|
5867
6050
|
const tempFile = path7.join(process.cwd(), ".effortless-config.mjs");
|
|
5868
6051
|
fs4.writeFileSync(tempFile, code);
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
}
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
};
|
|
6052
|
+
const mod = yield* Effect36.tryPromise({
|
|
6053
|
+
try: () => import(pathToFileURL(tempFile).href),
|
|
6054
|
+
catch: (error) => new Error(`Failed to load config: ${error}`)
|
|
6055
|
+
}).pipe(Effect36.ensuring(Effect36.sync(() => fs4.unlinkSync(tempFile))));
|
|
6056
|
+
return mod.default;
|
|
6057
|
+
});
|
|
5876
6058
|
var projectOption = Options.text("project").pipe(
|
|
5877
6059
|
Options.withAlias("p"),
|
|
5878
6060
|
Options.withDescription("Project name (or 'name' in effortless.config.ts)"),
|
|
@@ -5880,12 +6062,12 @@ var projectOption = Options.text("project").pipe(
|
|
|
5880
6062
|
);
|
|
5881
6063
|
var stageOption = Options.text("stage").pipe(
|
|
5882
6064
|
Options.withAlias("s"),
|
|
5883
|
-
Options.withDescription("
|
|
6065
|
+
Options.withDescription("Deployment stage (default: dev)"),
|
|
5884
6066
|
Options.withDefault("dev")
|
|
5885
6067
|
);
|
|
5886
6068
|
var regionOption = Options.text("region").pipe(
|
|
5887
6069
|
Options.withAlias("r"),
|
|
5888
|
-
Options.withDescription("AWS region"),
|
|
6070
|
+
Options.withDescription("AWS region (default: eu-central-1)"),
|
|
5889
6071
|
Options.withDefault("eu-central-1")
|
|
5890
6072
|
);
|
|
5891
6073
|
var verboseOption = Options.boolean("verbose").pipe(
|
|
@@ -5918,6 +6100,23 @@ var getPatternsFromConfig = (config) => {
|
|
|
5918
6100
|
});
|
|
5919
6101
|
};
|
|
5920
6102
|
|
|
6103
|
+
// src/cli/project-config.ts
|
|
6104
|
+
import * as Context13 from "effect/Context";
|
|
6105
|
+
import * as Layer14 from "effect/Layer";
|
|
6106
|
+
import * as Effect37 from "effect/Effect";
|
|
6107
|
+
import * as path8 from "path";
|
|
6108
|
+
var ProjectConfig = class _ProjectConfig extends Context13.Tag("ProjectConfig")() {
|
|
6109
|
+
static Live = Layer14.effect(
|
|
6110
|
+
_ProjectConfig,
|
|
6111
|
+
Effect37.gen(function* () {
|
|
6112
|
+
const config = yield* loadConfig();
|
|
6113
|
+
const cwd = process.cwd();
|
|
6114
|
+
const projectDir = config?.root ? path8.resolve(cwd, config.root) : cwd;
|
|
6115
|
+
return { config, cwd, projectDir };
|
|
6116
|
+
})
|
|
6117
|
+
);
|
|
6118
|
+
};
|
|
6119
|
+
|
|
5921
6120
|
// src/cli/commands/deploy.ts
|
|
5922
6121
|
var deployTargetArg = Args.text({ name: "target" }).pipe(
|
|
5923
6122
|
Args.withDescription("Handler name or file path to deploy (optional - uses config patterns if not specified)"),
|
|
@@ -5929,8 +6128,8 @@ var isFilePath = (target) => {
|
|
|
5929
6128
|
var deployCommand = Command.make(
|
|
5930
6129
|
"deploy",
|
|
5931
6130
|
{ target: deployTargetArg, project: projectOption, stage: stageOption, region: regionOption, verbose: verboseOption, noSites: noSitesOption },
|
|
5932
|
-
({ target, project: projectOpt, stage, region, verbose, noSites }) =>
|
|
5933
|
-
const config = yield*
|
|
6131
|
+
({ target, project: projectOpt, stage, region, verbose, noSites }) => Effect38.gen(function* () {
|
|
6132
|
+
const { config, cwd, projectDir } = yield* ProjectConfig;
|
|
5934
6133
|
const project = Option.getOrElse(projectOpt, () => config?.name ?? "");
|
|
5935
6134
|
const finalStage = config?.stage ?? stage;
|
|
5936
6135
|
const finalRegion = config?.region ?? region;
|
|
@@ -5949,9 +6148,8 @@ var deployCommand = Command.make(
|
|
|
5949
6148
|
acm: { region: "us-east-1" }
|
|
5950
6149
|
});
|
|
5951
6150
|
const logLevel = verbose ? LogLevel.Debug : LogLevel.Warning;
|
|
5952
|
-
const projectDir = process.cwd();
|
|
5953
6151
|
yield* Option.match(target, {
|
|
5954
|
-
onNone: () =>
|
|
6152
|
+
onNone: () => Effect38.gen(function* () {
|
|
5955
6153
|
const patterns = getPatternsFromConfig(config);
|
|
5956
6154
|
if (!patterns) {
|
|
5957
6155
|
yield* Console3.error("Error: No target specified and no 'handlers' patterns in config");
|
|
@@ -5959,13 +6157,15 @@ var deployCommand = Command.make(
|
|
|
5959
6157
|
}
|
|
5960
6158
|
const results = yield* deployProject({
|
|
5961
6159
|
projectDir,
|
|
6160
|
+
packageDir: cwd,
|
|
5962
6161
|
patterns,
|
|
5963
6162
|
project,
|
|
5964
6163
|
stage: finalStage,
|
|
5965
6164
|
region: finalRegion,
|
|
5966
|
-
noSites
|
|
6165
|
+
noSites,
|
|
6166
|
+
verbose
|
|
5967
6167
|
});
|
|
5968
|
-
const total = results.httpResults.length + results.tableResults.length + results.appResults.length + results.staticSiteResults.length;
|
|
6168
|
+
const total = results.httpResults.length + results.tableResults.length + results.appResults.length + results.staticSiteResults.length + results.apiResults.length;
|
|
5969
6169
|
yield* Console3.log(`
|
|
5970
6170
|
${c.green(`Deployed ${total} handler(s):`)}`);
|
|
5971
6171
|
if (results.apiUrl) {
|
|
@@ -5984,6 +6184,10 @@ ${c.green(`Deployed ${total} handler(s):`)}`);
|
|
|
5984
6184
|
for (const r of results.tableResults) {
|
|
5985
6185
|
summaryLines.push({ name: r.exportName, line: ` ${c.cyan("[table]")} ${c.bold(r.exportName)}` });
|
|
5986
6186
|
}
|
|
6187
|
+
for (const r of results.apiResults) {
|
|
6188
|
+
const pathPart = results.apiUrl ? r.url.replace(results.apiUrl, "") : r.url;
|
|
6189
|
+
summaryLines.push({ name: r.exportName, line: ` ${c.cyan("[api]")} ${c.bold(r.exportName)} ${c.dim(pathPart)}` });
|
|
6190
|
+
}
|
|
5987
6191
|
for (const r of results.staticSiteResults) {
|
|
5988
6192
|
summaryLines.push({ name: r.exportName, line: ` ${c.cyan("[site]")} ${c.bold(r.exportName)}: ${c.cyan(r.url)}` });
|
|
5989
6193
|
}
|
|
@@ -5992,26 +6196,27 @@ ${c.green(`Deployed ${total} handler(s):`)}`);
|
|
|
5992
6196
|
yield* Console3.log(line);
|
|
5993
6197
|
}
|
|
5994
6198
|
}),
|
|
5995
|
-
onSome: (targetValue) =>
|
|
6199
|
+
onSome: (targetValue) => Effect38.gen(function* () {
|
|
5996
6200
|
if (isFilePath(targetValue)) {
|
|
5997
|
-
const fullPath =
|
|
6201
|
+
const fullPath = path9.isAbsolute(targetValue) ? targetValue : path9.resolve(projectDir, targetValue);
|
|
5998
6202
|
const input = {
|
|
5999
6203
|
projectDir,
|
|
6204
|
+
packageDir: cwd,
|
|
6000
6205
|
file: fullPath,
|
|
6001
6206
|
project,
|
|
6002
6207
|
stage: finalStage,
|
|
6003
6208
|
region: finalRegion
|
|
6004
6209
|
};
|
|
6005
6210
|
const httpResult = yield* deployAll(input).pipe(
|
|
6006
|
-
|
|
6211
|
+
Effect38.catchIf(
|
|
6007
6212
|
(e) => e instanceof Error && e.message.includes("No defineHttp"),
|
|
6008
|
-
() =>
|
|
6213
|
+
() => Effect38.succeed(null)
|
|
6009
6214
|
)
|
|
6010
6215
|
);
|
|
6011
6216
|
const tableResults = yield* deployAllTables(input).pipe(
|
|
6012
|
-
|
|
6217
|
+
Effect38.catchIf(
|
|
6013
6218
|
(e) => e instanceof Error && e.message.includes("No defineTable"),
|
|
6014
|
-
() =>
|
|
6219
|
+
() => Effect38.succeed([])
|
|
6015
6220
|
)
|
|
6016
6221
|
);
|
|
6017
6222
|
if (!httpResult && tableResults.length === 0) {
|
|
@@ -6041,68 +6246,23 @@ Deployed ${tableResults.length} table handler(s):`));
|
|
|
6041
6246
|
}
|
|
6042
6247
|
const files = findHandlerFiles(patterns, projectDir);
|
|
6043
6248
|
const discovered = discoverHandlers(files);
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
for (const { file, exports } of discovered.httpHandlers) {
|
|
6048
|
-
for (const { exportName } of exports) {
|
|
6049
|
-
if (exportName === targetValue) {
|
|
6050
|
-
foundFile = file;
|
|
6051
|
-
foundExport = exportName;
|
|
6052
|
-
break;
|
|
6053
|
-
}
|
|
6054
|
-
}
|
|
6055
|
-
if (foundFile) break;
|
|
6056
|
-
}
|
|
6057
|
-
if (!foundFile) {
|
|
6058
|
-
for (const { file, exports } of discovered.tableHandlers) {
|
|
6059
|
-
for (const { exportName } of exports) {
|
|
6060
|
-
if (exportName === targetValue) {
|
|
6061
|
-
foundFile = file;
|
|
6062
|
-
foundExport = exportName;
|
|
6063
|
-
handlerType = "table";
|
|
6064
|
-
break;
|
|
6065
|
-
}
|
|
6066
|
-
}
|
|
6067
|
-
if (foundFile) break;
|
|
6068
|
-
}
|
|
6069
|
-
}
|
|
6070
|
-
if (!foundFile) {
|
|
6071
|
-
for (const { file, exports } of discovered.appHandlers) {
|
|
6072
|
-
for (const { exportName } of exports) {
|
|
6073
|
-
if (exportName === targetValue) {
|
|
6074
|
-
foundFile = file;
|
|
6075
|
-
foundExport = exportName;
|
|
6076
|
-
handlerType = "app";
|
|
6077
|
-
break;
|
|
6078
|
-
}
|
|
6079
|
-
}
|
|
6080
|
-
if (foundFile) break;
|
|
6081
|
-
}
|
|
6082
|
-
}
|
|
6083
|
-
if (!foundFile || !foundExport) {
|
|
6249
|
+
const allHandlers = flattenHandlers(discovered);
|
|
6250
|
+
const found = allHandlers.find((h) => h.exportName === targetValue);
|
|
6251
|
+
if (!found) {
|
|
6084
6252
|
yield* Console3.error(`Error: Handler "${targetValue}" not found`);
|
|
6085
6253
|
yield* Console3.log("\nAvailable handlers:");
|
|
6086
|
-
for (const
|
|
6087
|
-
|
|
6088
|
-
yield* Console3.log(` ${c.cyan("[http]")} ${exportName}`);
|
|
6089
|
-
}
|
|
6090
|
-
}
|
|
6091
|
-
for (const { exports } of discovered.tableHandlers) {
|
|
6092
|
-
for (const { exportName } of exports) {
|
|
6093
|
-
yield* Console3.log(` ${c.cyan("[table]")} ${exportName}`);
|
|
6094
|
-
}
|
|
6095
|
-
}
|
|
6096
|
-
for (const { exports } of discovered.appHandlers) {
|
|
6097
|
-
for (const { exportName } of exports) {
|
|
6098
|
-
yield* Console3.log(` ${c.cyan("[app]")} ${exportName}`);
|
|
6099
|
-
}
|
|
6254
|
+
for (const h of allHandlers) {
|
|
6255
|
+
yield* Console3.log(` ${c.cyan(`[${h.type}]`.padEnd(9))} ${h.exportName}`);
|
|
6100
6256
|
}
|
|
6101
6257
|
return;
|
|
6102
6258
|
}
|
|
6103
|
-
|
|
6259
|
+
const foundFile = found.file;
|
|
6260
|
+
const foundExport = found.exportName;
|
|
6261
|
+
const handlerType = found.type;
|
|
6262
|
+
yield* Console3.log(`Found handler ${c.bold(targetValue)} in ${c.dim(path9.relative(projectDir, foundFile))}`);
|
|
6104
6263
|
const input = {
|
|
6105
6264
|
projectDir,
|
|
6265
|
+
packageDir: cwd,
|
|
6106
6266
|
file: foundFile,
|
|
6107
6267
|
project,
|
|
6108
6268
|
stage: finalStage,
|
|
@@ -6121,15 +6281,15 @@ ${c.green("Deployed:")} ${c.cyan(result.url)}`);
|
|
|
6121
6281
|
}
|
|
6122
6282
|
})
|
|
6123
6283
|
}).pipe(
|
|
6124
|
-
|
|
6284
|
+
Effect38.provide(clientsLayer),
|
|
6125
6285
|
Logger.withMinimumLogLevel(logLevel)
|
|
6126
6286
|
);
|
|
6127
|
-
})
|
|
6128
|
-
).pipe(Command.withDescription("Deploy handlers
|
|
6287
|
+
}).pipe(Effect38.provide(ProjectConfig.Live))
|
|
6288
|
+
).pipe(Command.withDescription("Deploy handlers to AWS Lambda. Accepts a handler name, file path, or deploys all from config"));
|
|
6129
6289
|
|
|
6130
6290
|
// src/cli/commands/status.ts
|
|
6131
6291
|
import { Command as Command2 } from "@effect/cli";
|
|
6132
|
-
import { Effect as
|
|
6292
|
+
import { Effect as Effect39, Console as Console4, Logger as Logger2, LogLevel as LogLevel2, Option as Option2 } from "effect";
|
|
6133
6293
|
var { lambda, apigatewayv2: apigateway } = clients_exports;
|
|
6134
6294
|
var INTERNAL_HANDLERS = /* @__PURE__ */ new Set(["api", "platform"]);
|
|
6135
6295
|
var extractFunctionName = (arn) => {
|
|
@@ -6154,7 +6314,7 @@ var formatDate = (date) => {
|
|
|
6154
6314
|
if (days < 7) return `${days}d ago`;
|
|
6155
6315
|
return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
6156
6316
|
};
|
|
6157
|
-
var getLambdaDetails = (functionName) =>
|
|
6317
|
+
var getLambdaDetails = (functionName) => Effect39.gen(function* () {
|
|
6158
6318
|
const config = yield* lambda.make("get_function_configuration", {
|
|
6159
6319
|
FunctionName: functionName
|
|
6160
6320
|
});
|
|
@@ -6164,62 +6324,21 @@ var getLambdaDetails = (functionName) => Effect37.gen(function* () {
|
|
|
6164
6324
|
timeout: config.Timeout
|
|
6165
6325
|
};
|
|
6166
6326
|
}).pipe(
|
|
6167
|
-
|
|
6327
|
+
Effect39.catchAll(() => Effect39.succeed({}))
|
|
6168
6328
|
);
|
|
6169
|
-
var getApiUrl = (apiId) =>
|
|
6329
|
+
var getApiUrl = (apiId) => Effect39.gen(function* () {
|
|
6170
6330
|
const api = yield* apigateway.make("get_api", { ApiId: apiId });
|
|
6171
6331
|
return api.ApiEndpoint;
|
|
6172
6332
|
}).pipe(
|
|
6173
|
-
|
|
6333
|
+
Effect39.catchAll(() => Effect39.succeed(void 0))
|
|
6174
6334
|
);
|
|
6175
6335
|
var discoverCodeHandlers = (projectDir, patterns) => {
|
|
6176
6336
|
const files = findHandlerFiles(patterns, projectDir);
|
|
6177
6337
|
const discovered = discoverHandlers(files);
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
name: fn13.exportName,
|
|
6183
|
-
type: "http",
|
|
6184
|
-
method: fn13.config.method,
|
|
6185
|
-
path: fn13.config.path
|
|
6186
|
-
});
|
|
6187
|
-
}
|
|
6188
|
-
}
|
|
6189
|
-
for (const { exports } of discovered.tableHandlers) {
|
|
6190
|
-
for (const fn13 of exports) {
|
|
6191
|
-
handlers.push({
|
|
6192
|
-
name: fn13.exportName,
|
|
6193
|
-
type: "table"
|
|
6194
|
-
});
|
|
6195
|
-
}
|
|
6196
|
-
}
|
|
6197
|
-
for (const { exports } of discovered.appHandlers) {
|
|
6198
|
-
for (const fn13 of exports) {
|
|
6199
|
-
handlers.push({
|
|
6200
|
-
name: fn13.exportName,
|
|
6201
|
-
type: "app",
|
|
6202
|
-
path: fn13.config.path
|
|
6203
|
-
});
|
|
6204
|
-
}
|
|
6205
|
-
}
|
|
6206
|
-
for (const { exports } of discovered.staticSiteHandlers) {
|
|
6207
|
-
for (const fn13 of exports) {
|
|
6208
|
-
handlers.push({
|
|
6209
|
-
name: fn13.exportName,
|
|
6210
|
-
type: "site"
|
|
6211
|
-
});
|
|
6212
|
-
}
|
|
6213
|
-
}
|
|
6214
|
-
for (const { exports } of discovered.fifoQueueHandlers) {
|
|
6215
|
-
for (const fn13 of exports) {
|
|
6216
|
-
handlers.push({
|
|
6217
|
-
name: fn13.exportName,
|
|
6218
|
-
type: "queue"
|
|
6219
|
-
});
|
|
6220
|
-
}
|
|
6221
|
-
}
|
|
6222
|
-
return handlers;
|
|
6338
|
+
return flattenHandlers(discovered).map((h) => ({
|
|
6339
|
+
name: h.exportName,
|
|
6340
|
+
type: h.type
|
|
6341
|
+
}));
|
|
6223
6342
|
};
|
|
6224
6343
|
var discoverAwsHandlers = (resources) => {
|
|
6225
6344
|
const byHandler = groupResourcesByHandler(resources);
|
|
@@ -6247,6 +6366,7 @@ var TYPE_LABELS = {
|
|
|
6247
6366
|
http: "http",
|
|
6248
6367
|
table: "table",
|
|
6249
6368
|
app: "app",
|
|
6369
|
+
api: "api",
|
|
6250
6370
|
site: "site",
|
|
6251
6371
|
queue: "queue",
|
|
6252
6372
|
lambda: "lambda",
|
|
@@ -6267,9 +6387,9 @@ var STATUS_COLORS = {
|
|
|
6267
6387
|
var formatStatus = (status) => {
|
|
6268
6388
|
return STATUS_COLORS[status](status.padEnd(10));
|
|
6269
6389
|
};
|
|
6270
|
-
var formatRoute = (method,
|
|
6271
|
-
if (method &&
|
|
6272
|
-
if (
|
|
6390
|
+
var formatRoute = (method, path11) => {
|
|
6391
|
+
if (method && path11) return `${method.padEnd(5)} ${path11}`;
|
|
6392
|
+
if (path11) return path11;
|
|
6273
6393
|
return "";
|
|
6274
6394
|
};
|
|
6275
6395
|
var formatEntry = (entry) => {
|
|
@@ -6291,8 +6411,8 @@ var formatEntry = (entry) => {
|
|
|
6291
6411
|
var statusCommand = Command2.make(
|
|
6292
6412
|
"status",
|
|
6293
6413
|
{ project: projectOption, stage: stageOption, region: regionOption, verbose: verboseOption },
|
|
6294
|
-
({ project: projectOpt, stage, region, verbose }) =>
|
|
6295
|
-
const config = yield*
|
|
6414
|
+
({ project: projectOpt, stage, region, verbose }) => Effect39.gen(function* () {
|
|
6415
|
+
const { config, projectDir } = yield* ProjectConfig;
|
|
6296
6416
|
const project = Option2.getOrElse(projectOpt, () => config?.name ?? "");
|
|
6297
6417
|
const finalStage = config?.stage ?? stage;
|
|
6298
6418
|
const finalRegion = config?.region ?? region;
|
|
@@ -6306,11 +6426,10 @@ var statusCommand = Command2.make(
|
|
|
6306
6426
|
resource_groups_tagging_api: { region: finalRegion }
|
|
6307
6427
|
});
|
|
6308
6428
|
const logLevel = verbose ? LogLevel2.Debug : LogLevel2.Info;
|
|
6309
|
-
const projectDir = process.cwd();
|
|
6310
6429
|
const patterns = getPatternsFromConfig(config);
|
|
6311
6430
|
const codeHandlers = patterns ? discoverCodeHandlers(projectDir, patterns) : [];
|
|
6312
6431
|
const codeHandlerNames = new Set(codeHandlers.map((h) => h.name));
|
|
6313
|
-
yield*
|
|
6432
|
+
yield* Effect39.gen(function* () {
|
|
6314
6433
|
yield* Console4.log(`
|
|
6315
6434
|
Status for ${c.bold(project + "/" + finalStage)}:
|
|
6316
6435
|
`);
|
|
@@ -6407,18 +6526,18 @@ API: ${c.cyan(apiUrl)}`);
|
|
|
6407
6526
|
yield* Console4.log(`
|
|
6408
6527
|
Total: ${parts.join(", ")}`);
|
|
6409
6528
|
}).pipe(
|
|
6410
|
-
|
|
6529
|
+
Effect39.provide(clientsLayer),
|
|
6411
6530
|
Logger2.withMinimumLogLevel(logLevel)
|
|
6412
6531
|
);
|
|
6413
|
-
})
|
|
6414
|
-
).pipe(Command2.withDescription("
|
|
6532
|
+
}).pipe(Effect39.provide(ProjectConfig.Live))
|
|
6533
|
+
).pipe(Command2.withDescription("Compare local handlers with deployed AWS resources. Shows new, deployed, and orphaned handlers"));
|
|
6415
6534
|
|
|
6416
6535
|
// src/cli/commands/cleanup.ts
|
|
6417
6536
|
import { Command as Command3, Options as Options2 } from "@effect/cli";
|
|
6418
|
-
import { Effect as
|
|
6537
|
+
import { Effect as Effect41, Console as Console5, Logger as Logger3, LogLevel as LogLevel3, Option as Option3 } from "effect";
|
|
6419
6538
|
|
|
6420
6539
|
// src/deploy/cleanup.ts
|
|
6421
|
-
import { Effect as
|
|
6540
|
+
import { Effect as Effect40 } from "effect";
|
|
6422
6541
|
var extractResourceName = (arn, type) => {
|
|
6423
6542
|
switch (type) {
|
|
6424
6543
|
case "lambda": {
|
|
@@ -6461,7 +6580,7 @@ var extractLayerInfo = (arn) => {
|
|
|
6461
6580
|
version: parseInt(parts[parts.length - 1] ?? "0", 10)
|
|
6462
6581
|
};
|
|
6463
6582
|
};
|
|
6464
|
-
var deleteResource = (resource) =>
|
|
6583
|
+
var deleteResource = (resource) => Effect40.gen(function* () {
|
|
6465
6584
|
const name = extractResourceName(resource.arn, resource.type);
|
|
6466
6585
|
switch (resource.type) {
|
|
6467
6586
|
case "lambda":
|
|
@@ -6494,18 +6613,18 @@ var deleteResource = (resource) => Effect38.gen(function* () {
|
|
|
6494
6613
|
yield* deleteSesIdentity(name);
|
|
6495
6614
|
break;
|
|
6496
6615
|
default:
|
|
6497
|
-
yield*
|
|
6616
|
+
yield* Effect40.logWarning(`Unknown resource type: ${resource.type}, skipping ${resource.arn}`);
|
|
6498
6617
|
}
|
|
6499
6618
|
});
|
|
6500
|
-
var deleteResources = (resources) =>
|
|
6619
|
+
var deleteResources = (resources) => Effect40.gen(function* () {
|
|
6501
6620
|
const orderedTypes = ["lambda", "api-gateway", "cloudfront-distribution", "sqs", "ses", "dynamodb", "s3-bucket", "lambda-layer", "iam-role"];
|
|
6502
6621
|
const iamRolesToDelete = /* @__PURE__ */ new Set();
|
|
6503
6622
|
for (const type of orderedTypes) {
|
|
6504
6623
|
const resourcesOfType = resources.filter((r) => r.type === type);
|
|
6505
6624
|
for (const resource of resourcesOfType) {
|
|
6506
6625
|
yield* deleteResource(resource).pipe(
|
|
6507
|
-
|
|
6508
|
-
(error) =>
|
|
6626
|
+
Effect40.catchAll(
|
|
6627
|
+
(error) => Effect40.logError(`Failed to delete ${resource.type} ${resource.arn}: ${error}`)
|
|
6509
6628
|
)
|
|
6510
6629
|
);
|
|
6511
6630
|
if (resource.type === "lambda") {
|
|
@@ -6517,8 +6636,8 @@ var deleteResources = (resources) => Effect38.gen(function* () {
|
|
|
6517
6636
|
}
|
|
6518
6637
|
for (const roleName of iamRolesToDelete) {
|
|
6519
6638
|
yield* deleteRole(roleName).pipe(
|
|
6520
|
-
|
|
6521
|
-
(error) =>
|
|
6639
|
+
Effect40.catchAll(
|
|
6640
|
+
(error) => Effect40.logError(`Failed to delete IAM role ${roleName}: ${error}`)
|
|
6522
6641
|
)
|
|
6523
6642
|
);
|
|
6524
6643
|
}
|
|
@@ -6539,11 +6658,14 @@ var layerOption = Options2.boolean("layer").pipe(
|
|
|
6539
6658
|
var rolesOption = Options2.boolean("roles").pipe(
|
|
6540
6659
|
Options2.withDescription("Clean up orphaned IAM roles instead of handler resources")
|
|
6541
6660
|
);
|
|
6661
|
+
var orphanedOption = Options2.boolean("orphaned").pipe(
|
|
6662
|
+
Options2.withDescription("Delete only handlers that exist in AWS but not in code")
|
|
6663
|
+
);
|
|
6542
6664
|
var cleanupCommand = Command3.make(
|
|
6543
6665
|
"cleanup",
|
|
6544
|
-
{ project: projectOption, stage: stageOption, region: regionOption, handler: handlerOption, layer: layerOption, roles: rolesOption, all: cleanupAllOption, dryRun: dryRunOption, verbose: verboseOption },
|
|
6545
|
-
({ project: projectOpt, stage, region, handler: handlerOpt, layer: cleanupLayer, roles: cleanupRoles, all: deleteAll, dryRun, verbose }) =>
|
|
6546
|
-
const config = yield*
|
|
6666
|
+
{ project: projectOption, stage: stageOption, region: regionOption, handler: handlerOption, layer: layerOption, roles: rolesOption, orphaned: orphanedOption, all: cleanupAllOption, dryRun: dryRunOption, verbose: verboseOption },
|
|
6667
|
+
({ project: projectOpt, stage, region, handler: handlerOpt, layer: cleanupLayer, roles: cleanupRoles, orphaned: cleanupOrphaned, all: deleteAll, dryRun, verbose }) => Effect41.gen(function* () {
|
|
6668
|
+
const { config, projectDir } = yield* ProjectConfig;
|
|
6547
6669
|
const project = Option3.getOrElse(projectOpt, () => config?.name ?? "");
|
|
6548
6670
|
const finalStage = config?.stage ?? stage;
|
|
6549
6671
|
const finalRegion = config?.region ?? region;
|
|
@@ -6554,14 +6676,14 @@ var cleanupCommand = Command3.make(
|
|
|
6554
6676
|
const logLevel = verbose ? LogLevel3.Debug : LogLevel3.Info;
|
|
6555
6677
|
if (cleanupLayer) {
|
|
6556
6678
|
yield* cleanupLayerVersions({ project, region: finalRegion, deleteAll, dryRun }).pipe(
|
|
6557
|
-
|
|
6679
|
+
Effect41.provide(clients_exports.makeClients({ lambda: { region: finalRegion } })),
|
|
6558
6680
|
Logger3.withMinimumLogLevel(logLevel)
|
|
6559
6681
|
);
|
|
6560
6682
|
return;
|
|
6561
6683
|
}
|
|
6562
6684
|
if (cleanupRoles) {
|
|
6563
6685
|
yield* cleanupIamRoles({ project, stage: finalStage, region: finalRegion, deleteAll, dryRun }).pipe(
|
|
6564
|
-
|
|
6686
|
+
Effect41.provide(clients_exports.makeClients({ iam: { region: finalRegion } })),
|
|
6565
6687
|
Logger3.withMinimumLogLevel(logLevel)
|
|
6566
6688
|
);
|
|
6567
6689
|
return;
|
|
@@ -6576,7 +6698,7 @@ var cleanupCommand = Command3.make(
|
|
|
6576
6698
|
s3: { region: finalRegion },
|
|
6577
6699
|
cloudfront: { region: "us-east-1" }
|
|
6578
6700
|
});
|
|
6579
|
-
yield*
|
|
6701
|
+
yield* Effect41.gen(function* () {
|
|
6580
6702
|
yield* Console5.log(`
|
|
6581
6703
|
Looking for resources in ${c.bold(project + "/" + finalStage)}...
|
|
6582
6704
|
`);
|
|
@@ -6586,14 +6708,34 @@ Looking for resources in ${c.bold(project + "/" + finalStage)}...
|
|
|
6586
6708
|
return;
|
|
6587
6709
|
}
|
|
6588
6710
|
const byHandler = groupResourcesByHandler(resources);
|
|
6589
|
-
|
|
6590
|
-
if (
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6711
|
+
let handlersToDelete;
|
|
6712
|
+
if (cleanupOrphaned) {
|
|
6713
|
+
const patterns = getPatternsFromConfig(config);
|
|
6714
|
+
if (!patterns) {
|
|
6715
|
+
yield* Console5.error("Error: No 'handlers' patterns in config \u2014 cannot determine orphaned handlers");
|
|
6716
|
+
return;
|
|
6595
6717
|
}
|
|
6596
|
-
|
|
6718
|
+
const files = findHandlerFiles(patterns, projectDir);
|
|
6719
|
+
const discovered = discoverHandlers(files);
|
|
6720
|
+
const codeNames = new Set(flattenHandlers(discovered).map((h) => h.exportName));
|
|
6721
|
+
const INTERNAL_HANDLERS2 = /* @__PURE__ */ new Set(["api", "platform"]);
|
|
6722
|
+
handlersToDelete = Array.from(byHandler.entries()).filter(([name]) => !codeNames.has(name) && !INTERNAL_HANDLERS2.has(name));
|
|
6723
|
+
if (handlersToDelete.length === 0) {
|
|
6724
|
+
yield* Console5.log("No orphaned handlers found.");
|
|
6725
|
+
return;
|
|
6726
|
+
}
|
|
6727
|
+
} else if (handlerFilter) {
|
|
6728
|
+
if (!byHandler.has(handlerFilter)) {
|
|
6729
|
+
yield* Console5.error(`Handler "${handlerFilter}" not found.`);
|
|
6730
|
+
yield* Console5.log("\nAvailable handlers:");
|
|
6731
|
+
for (const [h] of byHandler) {
|
|
6732
|
+
yield* Console5.log(` - ${h}`);
|
|
6733
|
+
}
|
|
6734
|
+
return;
|
|
6735
|
+
}
|
|
6736
|
+
handlersToDelete = [[handlerFilter, byHandler.get(handlerFilter) ?? []]];
|
|
6737
|
+
} else {
|
|
6738
|
+
handlersToDelete = Array.from(byHandler.entries());
|
|
6597
6739
|
}
|
|
6598
6740
|
const resourcesToDelete = [];
|
|
6599
6741
|
const derivedRoles = [];
|
|
@@ -6624,9 +6766,10 @@ Total: ${totalResources} resource(s) (${derivedRoles.length} derived)`);
|
|
|
6624
6766
|
${c.yellow("[DRY RUN]")} No resources were deleted.`);
|
|
6625
6767
|
return;
|
|
6626
6768
|
}
|
|
6627
|
-
if (!handlerFilter && !deleteAll) {
|
|
6769
|
+
if (!handlerFilter && !cleanupOrphaned && !deleteAll) {
|
|
6628
6770
|
yield* Console5.log("\nTo delete these resources, use one of:");
|
|
6629
6771
|
yield* Console5.log(` ${c.dim("eff cleanup --all")} # Delete all resources`);
|
|
6772
|
+
yield* Console5.log(` ${c.dim("eff cleanup --orphaned")} # Delete orphaned handlers only`);
|
|
6630
6773
|
yield* Console5.log(` ${c.dim("eff cleanup --handler <name>")} # Delete specific handler`);
|
|
6631
6774
|
yield* Console5.log(` ${c.dim("eff cleanup --dry-run")} # Preview without deleting`);
|
|
6632
6775
|
return;
|
|
@@ -6635,12 +6778,12 @@ ${c.yellow("[DRY RUN]")} No resources were deleted.`);
|
|
|
6635
6778
|
yield* deleteResources(resourcesToDelete);
|
|
6636
6779
|
yield* Console5.log(c.green("\nDone!"));
|
|
6637
6780
|
}).pipe(
|
|
6638
|
-
|
|
6781
|
+
Effect41.provide(clientsLayer),
|
|
6639
6782
|
Logger3.withMinimumLogLevel(logLevel)
|
|
6640
6783
|
);
|
|
6641
|
-
})
|
|
6642
|
-
).pipe(Command3.withDescription("Delete deployed resources"));
|
|
6643
|
-
var cleanupLayerVersions = (input) =>
|
|
6784
|
+
}).pipe(Effect41.provide(ProjectConfig.Live))
|
|
6785
|
+
).pipe(Command3.withDescription("Delete deployed resources (Lambda, API Gateway, DynamoDB, IAM roles, layers)"));
|
|
6786
|
+
var cleanupLayerVersions = (input) => Effect41.gen(function* () {
|
|
6644
6787
|
const layerName = `${input.project}-deps`;
|
|
6645
6788
|
yield* Console5.log(`
|
|
6646
6789
|
Searching for layer versions: ${layerName}
|
|
@@ -6672,7 +6815,7 @@ ${c.yellow("[DRY RUN]")} No layers were deleted.`);
|
|
|
6672
6815
|
yield* Console5.log(c.green(`
|
|
6673
6816
|
Deleted ${deleted} layer version(s).`));
|
|
6674
6817
|
});
|
|
6675
|
-
var cleanupIamRoles = (input) =>
|
|
6818
|
+
var cleanupIamRoles = (input) => Effect41.gen(function* () {
|
|
6676
6819
|
yield* Console5.log("\nSearching for effortless IAM roles...\n");
|
|
6677
6820
|
const allRoles = yield* listEffortlessRoles();
|
|
6678
6821
|
if (allRoles.length === 0) {
|
|
@@ -6721,8 +6864,8 @@ ${c.yellow("[DRY RUN]")} No roles were deleted.`);
|
|
|
6721
6864
|
yield* Console5.log(c.red("\nDeleting roles..."));
|
|
6722
6865
|
for (const role of roles) {
|
|
6723
6866
|
yield* deleteRole(role.name).pipe(
|
|
6724
|
-
|
|
6725
|
-
(error) =>
|
|
6867
|
+
Effect41.catchAll(
|
|
6868
|
+
(error) => Effect41.logError(`Failed to delete ${role.name}: ${error}`)
|
|
6726
6869
|
)
|
|
6727
6870
|
);
|
|
6728
6871
|
}
|
|
@@ -6731,7 +6874,7 @@ ${c.yellow("[DRY RUN]")} No roles were deleted.`);
|
|
|
6731
6874
|
|
|
6732
6875
|
// src/cli/commands/logs.ts
|
|
6733
6876
|
import { Args as Args2, Command as Command4, Options as Options3 } from "@effect/cli";
|
|
6734
|
-
import { Effect as
|
|
6877
|
+
import { Effect as Effect42, Console as Console6, Logger as Logger4, LogLevel as LogLevel4, Option as Option4, Schedule as Schedule4 } from "effect";
|
|
6735
6878
|
var { cloudwatch_logs } = clients_exports;
|
|
6736
6879
|
var handlerArg = Args2.text({ name: "handler" }).pipe(
|
|
6737
6880
|
Args2.withDescription("Handler name to show logs for")
|
|
@@ -6802,8 +6945,8 @@ var fetchLogs = (logGroupName, startTime, nextToken) => cloudwatch_logs.make("fi
|
|
|
6802
6945
|
var logsCommand = Command4.make(
|
|
6803
6946
|
"logs",
|
|
6804
6947
|
{ handler: handlerArg, project: projectOption, stage: stageOption, region: regionOption, tail: tailOption, since: sinceOption, verbose: verboseOption },
|
|
6805
|
-
({ handler: handlerName, project: projectOpt, stage, region, tail, since, verbose }) =>
|
|
6806
|
-
const config = yield*
|
|
6948
|
+
({ handler: handlerName, project: projectOpt, stage, region, tail, since, verbose }) => Effect42.gen(function* () {
|
|
6949
|
+
const { config, projectDir } = yield* ProjectConfig;
|
|
6807
6950
|
const project = Option4.getOrElse(projectOpt, () => config?.name ?? "");
|
|
6808
6951
|
const finalStage = config?.stage ?? stage;
|
|
6809
6952
|
const finalRegion = config?.region ?? region;
|
|
@@ -6811,17 +6954,11 @@ var logsCommand = Command4.make(
|
|
|
6811
6954
|
yield* Console6.error("Error: --project is required (or set 'name' in effortless.config.ts)");
|
|
6812
6955
|
return;
|
|
6813
6956
|
}
|
|
6814
|
-
const projectDir = process.cwd();
|
|
6815
6957
|
const patterns = getPatternsFromConfig(config);
|
|
6816
6958
|
if (patterns) {
|
|
6817
6959
|
const files = findHandlerFiles(patterns, projectDir);
|
|
6818
6960
|
const discovered = discoverHandlers(files);
|
|
6819
|
-
const allHandlerNames =
|
|
6820
|
-
...discovered.httpHandlers.flatMap((h) => h.exports.map((e) => e.exportName)),
|
|
6821
|
-
...discovered.tableHandlers.flatMap((h) => h.exports.map((e) => e.exportName)),
|
|
6822
|
-
...discovered.appHandlers.flatMap((h) => h.exports.map((e) => e.exportName)),
|
|
6823
|
-
...discovered.fifoQueueHandlers.flatMap((h) => h.exports.map((e) => e.exportName))
|
|
6824
|
-
];
|
|
6961
|
+
const allHandlerNames = flattenHandlers(discovered).map((h) => h.exportName);
|
|
6825
6962
|
if (!allHandlerNames.includes(handlerName)) {
|
|
6826
6963
|
yield* Console6.error(`Handler "${handlerName}" not found in code.`);
|
|
6827
6964
|
if (allHandlerNames.length > 0) {
|
|
@@ -6839,18 +6976,18 @@ var logsCommand = Command4.make(
|
|
|
6839
6976
|
cloudwatch_logs: { region: finalRegion }
|
|
6840
6977
|
});
|
|
6841
6978
|
const logLevel = verbose ? LogLevel4.Debug : LogLevel4.Info;
|
|
6842
|
-
yield*
|
|
6979
|
+
yield* Effect42.gen(function* () {
|
|
6843
6980
|
const durationMs = parseDuration(since);
|
|
6844
6981
|
let startTime = Date.now() - durationMs;
|
|
6845
6982
|
yield* Console6.log(`Logs for ${c.bold(handlerName)} ${c.dim(`(${logGroupName})`)}:
|
|
6846
6983
|
`);
|
|
6847
6984
|
let hasLogs = false;
|
|
6848
6985
|
const result = yield* fetchLogs(logGroupName, startTime).pipe(
|
|
6849
|
-
|
|
6986
|
+
Effect42.catchAll((error) => {
|
|
6850
6987
|
if (error instanceof clients_exports.cloudwatch_logs.CloudWatchLogsError && error.cause.name === "ResourceNotFoundException") {
|
|
6851
|
-
return
|
|
6988
|
+
return Effect42.succeed({ events: void 0, nextToken: void 0 });
|
|
6852
6989
|
}
|
|
6853
|
-
return
|
|
6990
|
+
return Effect42.fail(error);
|
|
6854
6991
|
})
|
|
6855
6992
|
);
|
|
6856
6993
|
if (result.events && result.events.length > 0) {
|
|
@@ -6874,10 +7011,10 @@ var logsCommand = Command4.make(
|
|
|
6874
7011
|
if (!hasLogs) {
|
|
6875
7012
|
yield* Console6.log("Waiting for logs... (Ctrl+C to stop)\n");
|
|
6876
7013
|
}
|
|
6877
|
-
yield*
|
|
6878
|
-
|
|
7014
|
+
yield* Effect42.repeat(
|
|
7015
|
+
Effect42.gen(function* () {
|
|
6879
7016
|
const result2 = yield* fetchLogs(logGroupName, startTime).pipe(
|
|
6880
|
-
|
|
7017
|
+
Effect42.catchAll(() => Effect42.succeed({ events: void 0, nextToken: void 0 }))
|
|
6881
7018
|
);
|
|
6882
7019
|
if (result2.events && result2.events.length > 0) {
|
|
6883
7020
|
for (const event of result2.events) {
|
|
@@ -6895,16 +7032,16 @@ var logsCommand = Command4.make(
|
|
|
6895
7032
|
Schedule4.spaced("2 seconds")
|
|
6896
7033
|
);
|
|
6897
7034
|
}).pipe(
|
|
6898
|
-
|
|
7035
|
+
Effect42.provide(clientsLayer),
|
|
6899
7036
|
Logger4.withMinimumLogLevel(logLevel)
|
|
6900
7037
|
);
|
|
6901
|
-
})
|
|
6902
|
-
).pipe(Command4.withDescription("
|
|
7038
|
+
}).pipe(Effect42.provide(ProjectConfig.Live))
|
|
7039
|
+
).pipe(Command4.withDescription("Stream CloudWatch logs for a handler. Supports --tail for live tailing and --since for time range"));
|
|
6903
7040
|
|
|
6904
7041
|
// src/cli/commands/layer.ts
|
|
6905
7042
|
import { Command as Command5, Options as Options4 } from "@effect/cli";
|
|
6906
|
-
import { Effect as
|
|
6907
|
-
import * as
|
|
7043
|
+
import { Effect as Effect43, Console as Console7 } from "effect";
|
|
7044
|
+
import * as path10 from "path";
|
|
6908
7045
|
import * as fs5 from "fs";
|
|
6909
7046
|
var buildOption = Options4.boolean("build").pipe(
|
|
6910
7047
|
Options4.withDescription("Build layer directory locally (for debugging)")
|
|
@@ -6912,22 +7049,28 @@ var buildOption = Options4.boolean("build").pipe(
|
|
|
6912
7049
|
var layerCommand = Command5.make(
|
|
6913
7050
|
"layer",
|
|
6914
7051
|
{ build: buildOption, output: outputOption, verbose: verboseOption },
|
|
6915
|
-
({ build: build3, output, verbose }) =>
|
|
6916
|
-
const config = yield*
|
|
6917
|
-
const projectDir = process.cwd();
|
|
7052
|
+
({ build: build3, output, verbose }) => Effect43.gen(function* () {
|
|
7053
|
+
const { config, cwd } = yield* ProjectConfig;
|
|
6918
7054
|
if (build3) {
|
|
6919
|
-
yield* buildLayer(
|
|
7055
|
+
yield* buildLayer(cwd, output, verbose);
|
|
6920
7056
|
} else {
|
|
6921
|
-
yield* showLayerInfo(
|
|
7057
|
+
yield* showLayerInfo(cwd, config?.name, verbose);
|
|
6922
7058
|
}
|
|
6923
|
-
})
|
|
6924
|
-
).pipe(Command5.withDescription("
|
|
6925
|
-
var showLayerInfo = (projectDir, projectName, verbose) =>
|
|
7059
|
+
}).pipe(Effect43.provide(ProjectConfig.Live))
|
|
7060
|
+
).pipe(Command5.withDescription("Inspect or locally build the shared Lambda dependency layer from package.json"));
|
|
7061
|
+
var showLayerInfo = (projectDir, projectName, verbose) => Effect43.gen(function* () {
|
|
6926
7062
|
yield* Console7.log(`
|
|
6927
7063
|
${c.bold("=== Layer Packages Preview ===")}
|
|
6928
7064
|
`);
|
|
7065
|
+
const depWarnings = yield* checkDependencyWarnings(projectDir).pipe(
|
|
7066
|
+
Effect43.catchAll(() => Effect43.succeed([]))
|
|
7067
|
+
);
|
|
7068
|
+
for (const w of depWarnings) {
|
|
7069
|
+
yield* Console7.log(c.yellow(` \u26A0 ${w}`));
|
|
7070
|
+
}
|
|
7071
|
+
if (depWarnings.length > 0) yield* Console7.log("");
|
|
6929
7072
|
const prodDeps = yield* readProductionDependencies(projectDir).pipe(
|
|
6930
|
-
|
|
7073
|
+
Effect43.catchAll(() => Effect43.succeed([]))
|
|
6931
7074
|
);
|
|
6932
7075
|
if (prodDeps.length === 0) {
|
|
6933
7076
|
yield* Console7.log("No production dependencies found in package.json");
|
|
@@ -6939,7 +7082,7 @@ ${c.bold("=== Layer Packages Preview ===")}
|
|
|
6939
7082
|
yield* Console7.log(` ${dep}`);
|
|
6940
7083
|
}
|
|
6941
7084
|
const hash = yield* computeLockfileHash(projectDir).pipe(
|
|
6942
|
-
|
|
7085
|
+
Effect43.catchAll(() => Effect43.succeed(null))
|
|
6943
7086
|
);
|
|
6944
7087
|
if (hash) {
|
|
6945
7088
|
yield* Console7.log(`
|
|
@@ -6947,7 +7090,7 @@ Lockfile hash: ${hash}`);
|
|
|
6947
7090
|
} else {
|
|
6948
7091
|
yield* Console7.log("\nNo lockfile found (package-lock.json, pnpm-lock.yaml, or yarn.lock)");
|
|
6949
7092
|
}
|
|
6950
|
-
const { packages: allPackages, warnings: layerWarnings } = yield*
|
|
7093
|
+
const { packages: allPackages, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps));
|
|
6951
7094
|
if (layerWarnings.length > 0) {
|
|
6952
7095
|
yield* Console7.log(c.yellow(`
|
|
6953
7096
|
Warnings (${layerWarnings.length}):`));
|
|
@@ -6976,10 +7119,10 @@ Total packages for layer ${c.dim(`(${allPackages.length})`)}:`);
|
|
|
6976
7119
|
Layer name: ${projectName}-deps`);
|
|
6977
7120
|
}
|
|
6978
7121
|
});
|
|
6979
|
-
var buildLayer = (projectDir, output, verbose) =>
|
|
6980
|
-
const outputDir =
|
|
6981
|
-
const layerDir =
|
|
6982
|
-
const layerRoot =
|
|
7122
|
+
var buildLayer = (projectDir, output, verbose) => Effect43.gen(function* () {
|
|
7123
|
+
const outputDir = path10.isAbsolute(output) ? output : path10.resolve(projectDir, output);
|
|
7124
|
+
const layerDir = path10.join(outputDir, "nodejs", "node_modules");
|
|
7125
|
+
const layerRoot = path10.join(outputDir, "nodejs");
|
|
6983
7126
|
if (fs5.existsSync(layerRoot)) {
|
|
6984
7127
|
fs5.rmSync(layerRoot, { recursive: true });
|
|
6985
7128
|
}
|
|
@@ -6987,8 +7130,15 @@ var buildLayer = (projectDir, output, verbose) => Effect41.gen(function* () {
|
|
|
6987
7130
|
yield* Console7.log(`
|
|
6988
7131
|
${c.bold("=== Building Layer Locally ===")}
|
|
6989
7132
|
`);
|
|
7133
|
+
const depWarnings = yield* checkDependencyWarnings(projectDir).pipe(
|
|
7134
|
+
Effect43.catchAll(() => Effect43.succeed([]))
|
|
7135
|
+
);
|
|
7136
|
+
for (const w of depWarnings) {
|
|
7137
|
+
yield* Console7.log(c.yellow(` \u26A0 ${w}`));
|
|
7138
|
+
}
|
|
7139
|
+
if (depWarnings.length > 0) yield* Console7.log("");
|
|
6990
7140
|
const prodDeps = yield* readProductionDependencies(projectDir).pipe(
|
|
6991
|
-
|
|
7141
|
+
Effect43.catchAll(() => Effect43.succeed([]))
|
|
6992
7142
|
);
|
|
6993
7143
|
if (prodDeps.length === 0) {
|
|
6994
7144
|
yield* Console7.log("No production dependencies found in package.json");
|
|
@@ -7000,11 +7150,11 @@ ${c.bold("=== Building Layer Locally ===")}
|
|
|
7000
7150
|
yield* Console7.log(` ${dep}`);
|
|
7001
7151
|
}
|
|
7002
7152
|
const hash = yield* computeLockfileHash(projectDir).pipe(
|
|
7003
|
-
|
|
7153
|
+
Effect43.catchAll(() => Effect43.succeed("unknown"))
|
|
7004
7154
|
);
|
|
7005
7155
|
yield* Console7.log(`
|
|
7006
7156
|
Lockfile hash: ${hash}`);
|
|
7007
|
-
const { packages: allPackages, resolvedPaths, warnings: layerWarnings } = yield*
|
|
7157
|
+
const { packages: allPackages, resolvedPaths, warnings: layerWarnings } = yield* Effect43.sync(() => collectLayerPackages(projectDir, prodDeps));
|
|
7008
7158
|
if (layerWarnings.length > 0) {
|
|
7009
7159
|
yield* Console7.log(`
|
|
7010
7160
|
Warnings (${layerWarnings.length}):`);
|
|
@@ -7031,9 +7181,9 @@ Collected ${allPackages.length} packages for layer`);
|
|
|
7031
7181
|
}
|
|
7032
7182
|
continue;
|
|
7033
7183
|
}
|
|
7034
|
-
const destPath =
|
|
7184
|
+
const destPath = path10.join(layerDir, pkgName);
|
|
7035
7185
|
if (pkgName.startsWith("@")) {
|
|
7036
|
-
const scopeDir =
|
|
7186
|
+
const scopeDir = path10.join(layerDir, pkgName.split("/")[0] ?? pkgName);
|
|
7037
7187
|
if (!fs5.existsSync(scopeDir)) {
|
|
7038
7188
|
fs5.mkdirSync(scopeDir, { recursive: true });
|
|
7039
7189
|
}
|
|
@@ -7054,20 +7204,20 @@ To inspect: ls ${layerDir}`);
|
|
|
7054
7204
|
// src/cli/commands/config.ts
|
|
7055
7205
|
import { Args as Args3, Command as Command6 } from "@effect/cli";
|
|
7056
7206
|
import { Prompt } from "@effect/cli";
|
|
7057
|
-
import { Effect as
|
|
7058
|
-
var loadRequiredParams = (projectOpt, stage, region) =>
|
|
7059
|
-
const config = yield*
|
|
7207
|
+
import { Effect as Effect44, Console as Console8, Logger as Logger5, LogLevel as LogLevel5, Option as Option5 } from "effect";
|
|
7208
|
+
var loadRequiredParams = (projectOpt, stage, region) => Effect44.gen(function* () {
|
|
7209
|
+
const { config, projectDir } = yield* ProjectConfig;
|
|
7060
7210
|
const project = Option5.getOrElse(projectOpt, () => config?.name ?? "");
|
|
7061
7211
|
if (!project) {
|
|
7062
7212
|
yield* Console8.error("Error: --project is required (or set 'name' in effortless.config.ts)");
|
|
7063
|
-
return yield*
|
|
7213
|
+
return yield* Effect44.fail(new Error("Missing project name"));
|
|
7064
7214
|
}
|
|
7065
7215
|
const patterns = getPatternsFromConfig(config);
|
|
7066
7216
|
if (!patterns) {
|
|
7067
7217
|
yield* Console8.error("Error: No 'handlers' patterns in config");
|
|
7068
|
-
return yield*
|
|
7218
|
+
return yield* Effect44.fail(new Error("Missing handler patterns"));
|
|
7069
7219
|
}
|
|
7070
|
-
const files = findHandlerFiles(patterns,
|
|
7220
|
+
const files = findHandlerFiles(patterns, projectDir);
|
|
7071
7221
|
const handlers = discoverHandlers(files);
|
|
7072
7222
|
const finalStage = config?.stage ?? stage;
|
|
7073
7223
|
const finalRegion = config?.region ?? region;
|
|
@@ -7077,7 +7227,7 @@ var loadRequiredParams = (projectOpt, stage, region) => Effect42.gen(function* (
|
|
|
7077
7227
|
var listCommand = Command6.make(
|
|
7078
7228
|
"list",
|
|
7079
7229
|
{ project: projectOption, stage: stageOption, region: regionOption, verbose: verboseOption },
|
|
7080
|
-
({ project: projectOpt, stage, region, verbose }) =>
|
|
7230
|
+
({ project: projectOpt, stage, region, verbose }) => Effect44.gen(function* () {
|
|
7081
7231
|
const ctx = yield* loadRequiredParams(projectOpt, stage, region);
|
|
7082
7232
|
const { params } = ctx;
|
|
7083
7233
|
if (params.length === 0) {
|
|
@@ -7085,7 +7235,7 @@ var listCommand = Command6.make(
|
|
|
7085
7235
|
return;
|
|
7086
7236
|
}
|
|
7087
7237
|
const { existing, missing } = yield* checkMissingParams(params).pipe(
|
|
7088
|
-
|
|
7238
|
+
Effect44.provide(clients_exports.makeClients({ ssm: { region: ctx.region } }))
|
|
7089
7239
|
);
|
|
7090
7240
|
yield* Console8.log(`
|
|
7091
7241
|
${c.bold("Config parameters")} ${c.dim(`(${ctx.project} / ${ctx.stage})`)}
|
|
@@ -7108,16 +7258,19 @@ ${c.bold("Config parameters")} ${c.dim(`(${ctx.project} / ${ctx.stage})`)}
|
|
|
7108
7258
|
${c.green("All parameters are set.")}`);
|
|
7109
7259
|
}
|
|
7110
7260
|
yield* Console8.log("");
|
|
7111
|
-
}).pipe(
|
|
7112
|
-
|
|
7261
|
+
}).pipe(
|
|
7262
|
+
Effect44.provide(ProjectConfig.Live),
|
|
7263
|
+
Logger5.withMinimumLogLevel(LogLevel5.Warning)
|
|
7264
|
+
)
|
|
7265
|
+
).pipe(Command6.withDescription("List all declared config parameters and show which are set vs missing"));
|
|
7113
7266
|
var setKeyArg = Args3.text({ name: "key" }).pipe(
|
|
7114
7267
|
Args3.withDescription("SSM parameter key (e.g. stripe/secret-key)")
|
|
7115
7268
|
);
|
|
7116
7269
|
var setCommand = Command6.make(
|
|
7117
7270
|
"set",
|
|
7118
7271
|
{ key: setKeyArg, project: projectOption, stage: stageOption, region: regionOption, verbose: verboseOption },
|
|
7119
|
-
({ key, project: projectOpt, stage, region, verbose }) =>
|
|
7120
|
-
const config = yield*
|
|
7272
|
+
({ key, project: projectOpt, stage, region, verbose }) => Effect44.gen(function* () {
|
|
7273
|
+
const { config } = yield* ProjectConfig;
|
|
7121
7274
|
const project = Option5.getOrElse(projectOpt, () => config?.name ?? "");
|
|
7122
7275
|
if (!project) {
|
|
7123
7276
|
yield* Console8.error("Error: --project is required (or set 'name' in effortless.config.ts)");
|
|
@@ -7134,15 +7287,18 @@ var setCommand = Command6.make(
|
|
|
7134
7287
|
Value: value,
|
|
7135
7288
|
Type: "SecureString",
|
|
7136
7289
|
Overwrite: true
|
|
7137
|
-
}).pipe(
|
|
7290
|
+
}).pipe(Effect44.provide(clients_exports.makeClients({ ssm: { region: finalRegion } })));
|
|
7138
7291
|
yield* Console8.log(`
|
|
7139
7292
|
${c.green("\u2713")} ${c.cyan(ssmPath)} ${c.dim("(SecureString)")}`);
|
|
7140
|
-
}).pipe(
|
|
7141
|
-
|
|
7293
|
+
}).pipe(
|
|
7294
|
+
Effect44.provide(ProjectConfig.Live),
|
|
7295
|
+
Logger5.withMinimumLogLevel(LogLevel5.Warning)
|
|
7296
|
+
)
|
|
7297
|
+
).pipe(Command6.withDescription("Set a config parameter value (stored encrypted in AWS)"));
|
|
7142
7298
|
var configRootCommand = Command6.make(
|
|
7143
7299
|
"config",
|
|
7144
7300
|
{ project: projectOption, stage: stageOption, region: regionOption, verbose: verboseOption },
|
|
7145
|
-
({ project: projectOpt, stage, region, verbose }) =>
|
|
7301
|
+
({ project: projectOpt, stage, region, verbose }) => Effect44.gen(function* () {
|
|
7146
7302
|
const ctx = yield* loadRequiredParams(projectOpt, stage, region);
|
|
7147
7303
|
const { params } = ctx;
|
|
7148
7304
|
if (params.length === 0) {
|
|
@@ -7150,7 +7306,7 @@ var configRootCommand = Command6.make(
|
|
|
7150
7306
|
return;
|
|
7151
7307
|
}
|
|
7152
7308
|
const { missing } = yield* checkMissingParams(params).pipe(
|
|
7153
|
-
|
|
7309
|
+
Effect44.provide(clients_exports.makeClients({ ssm: { region: ctx.region } }))
|
|
7154
7310
|
);
|
|
7155
7311
|
if (missing.length === 0) {
|
|
7156
7312
|
yield* Console8.log(`
|
|
@@ -7175,7 +7331,7 @@ ${c.bold("Missing parameters")} ${c.dim(`(${ctx.project} / ${ctx.stage})`)}
|
|
|
7175
7331
|
Value: value,
|
|
7176
7332
|
Type: "SecureString",
|
|
7177
7333
|
Overwrite: false
|
|
7178
|
-
}).pipe(
|
|
7334
|
+
}).pipe(Effect44.provide(clients_exports.makeClients({ ssm: { region: ctx.region } })));
|
|
7179
7335
|
yield* Console8.log(` ${c.green("\u2713")} created`);
|
|
7180
7336
|
created++;
|
|
7181
7337
|
}
|
|
@@ -7188,9 +7344,12 @@ ${c.bold("Missing parameters")} ${c.dim(`(${ctx.project} / ${ctx.stage})`)}
|
|
|
7188
7344
|
No parameters created.
|
|
7189
7345
|
`);
|
|
7190
7346
|
}
|
|
7191
|
-
}).pipe(
|
|
7347
|
+
}).pipe(
|
|
7348
|
+
Effect44.provide(ProjectConfig.Live),
|
|
7349
|
+
Logger5.withMinimumLogLevel(LogLevel5.Warning)
|
|
7350
|
+
)
|
|
7192
7351
|
).pipe(
|
|
7193
|
-
Command6.withDescription("Manage
|
|
7352
|
+
Command6.withDescription("Manage config values declared via param() in handlers. Run without subcommand to interactively set missing values"),
|
|
7194
7353
|
Command6.withSubcommands([listCommand, setCommand])
|
|
7195
7354
|
);
|
|
7196
7355
|
var configCommand = configRootCommand;
|
|
@@ -7207,6 +7366,7 @@ var cli = Command7.run(mainCommand, {
|
|
|
7207
7366
|
version
|
|
7208
7367
|
});
|
|
7209
7368
|
cli(process.argv).pipe(
|
|
7210
|
-
|
|
7369
|
+
Effect45.provide(NodeContext.layer),
|
|
7370
|
+
Effect45.provide(CliConfig.layer({ showBuiltIns: false, showTypes: false })),
|
|
7211
7371
|
NodeRuntime.runMain
|
|
7212
7372
|
);
|