@effortless-aws/cli 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli/index.js +101 -43
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -1943,56 +1943,36 @@ var ensureFunctionUrl = (functionName) => Effect13.gen(function* () {
1943
1943
  )
1944
1944
  );
1945
1945
  if (existing) {
1946
- yield* Effect13.logDebug(`Function URL already exists: ${existing.FunctionUrl}`);
1946
+ yield* lambda_exports.make("update_function_url_config", {
1947
+ FunctionName: functionName,
1948
+ AuthType: "NONE"
1949
+ });
1947
1950
  return { functionUrl: existing.FunctionUrl };
1948
1951
  }
1949
1952
  yield* Effect13.logDebug(`Creating Function URL for: ${functionName}`);
1950
1953
  const result = yield* lambda_exports.make("create_function_url_config", {
1951
1954
  FunctionName: functionName,
1952
- AuthType: "AWS_IAM",
1955
+ AuthType: "NONE",
1953
1956
  InvokeMode: "BUFFERED"
1954
1957
  });
1955
1958
  return { functionUrl: result.FunctionUrl };
1956
1959
  });
1957
- var addCloudFrontPermission = (functionName, distributionArn) => Effect13.gen(function* () {
1960
+ var addFunctionUrlPublicAccess = (functionName) => Effect13.gen(function* () {
1958
1961
  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
- }
1962
+ { statementId: "function-url-public", action: "lambda:InvokeFunctionUrl", urlAuthType: "NONE" },
1963
+ { statementId: "function-url-invoke", action: "lambda:InvokeFunction", urlAuthType: void 0 }
1969
1964
  ];
1970
1965
  for (const stmt of statements) {
1971
1966
  yield* lambda_exports.make("add_permission", {
1972
1967
  FunctionName: functionName,
1973
1968
  StatementId: stmt.statementId,
1974
1969
  Action: stmt.action,
1975
- Principal: "cloudfront.amazonaws.com",
1976
- SourceArn: distributionArn,
1977
- ...stmt.authType ? { FunctionUrlAuthType: stmt.authType } : {}
1970
+ Principal: "*",
1971
+ ...stmt.urlAuthType ? { FunctionUrlAuthType: stmt.urlAuthType } : {}
1978
1972
  }).pipe(
1979
1973
  Effect13.catchIf(
1980
1974
  (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
- })
1975
+ () => Effect13.logDebug(`Permission ${stmt.statementId} already exists for ${functionName}`)
1996
1976
  )
1997
1977
  );
1998
1978
  }
@@ -2660,6 +2640,14 @@ var DEV_ONLY_PREFIXES = [
2660
2640
  "@vitest/",
2661
2641
  "@jest/"
2662
2642
  ];
2643
+ var extractExportPaths = (value) => {
2644
+ if (typeof value === "string") return [value];
2645
+ if (typeof value === "object" && value !== null) {
2646
+ return Object.values(value).flatMap(extractExportPaths);
2647
+ }
2648
+ return [];
2649
+ };
2650
+ var isRawTypeScript = (p) => /\.(?:ts|tsx|mts|cts)$/.test(p) && !p.endsWith(".d.ts") && !p.endsWith(".d.mts") && !p.endsWith(".d.cts");
2663
2651
  var checkDependencyWarnings = (projectDir) => Effect18.gen(function* () {
2664
2652
  const pkgPath = path.join(projectDir, "package.json");
2665
2653
  const content = yield* Effect18.tryPromise({
@@ -2684,6 +2672,26 @@ var checkDependencyWarnings = (projectDir) => Effect18.gen(function* () {
2684
2672
  `"dependencies" is empty but "devDependencies" has ${devDeps.length} package(s). Runtime packages must be in "dependencies" to be included in the Lambda layer.`
2685
2673
  );
2686
2674
  }
2675
+ for (const dep of deps) {
2676
+ const depPath = getPackageRealPath(projectDir, dep);
2677
+ if (!depPath) continue;
2678
+ const depPkgPath = path.join(depPath, "package.json");
2679
+ if (!fsSync.existsSync(depPkgPath)) continue;
2680
+ try {
2681
+ const depPkg = JSON.parse(fsSync.readFileSync(depPkgPath, "utf-8"));
2682
+ const entryPoints = [];
2683
+ if (typeof depPkg.main === "string") entryPoints.push(depPkg.main);
2684
+ if (typeof depPkg.module === "string") entryPoints.push(depPkg.module);
2685
+ entryPoints.push(...extractExportPaths(depPkg.exports));
2686
+ const tsEntries = [...new Set(entryPoints.filter(isRawTypeScript))];
2687
+ if (tsEntries.length > 0) {
2688
+ warnings.push(
2689
+ `Package "${dep}" has TypeScript entry points (${tsEntries.join(", ")}) that will fail at runtime in node_modules (ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING). Move it to "devDependencies" so esbuild can bundle and transpile it.`
2690
+ );
2691
+ }
2692
+ } catch {
2693
+ }
2694
+ }
2687
2695
  return warnings;
2688
2696
  });
2689
2697
  var getPackageRealPath = (projectDir, pkgName) => {
@@ -3258,6 +3266,16 @@ var CACHING_OPTIMIZED_POLICY_ID = "658327ea-f89d-4fab-a63d-7e88639e58f6";
3258
3266
  var CACHING_DISABLED_POLICY_ID = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad";
3259
3267
  var ALL_VIEWER_EXCEPT_HOST_HEADER_POLICY_ID = "b689b0a8-53d0-40ab-baf2-68738e2966ac";
3260
3268
  var SECURITY_HEADERS_POLICY_ID = "67f7725c-6f97-4210-82d7-5512b31e9d03";
3269
+ var expandRoutePatterns = (patterns) => {
3270
+ const expanded = /* @__PURE__ */ new Set();
3271
+ for (const p of patterns) {
3272
+ expanded.add(p);
3273
+ if (p.endsWith("/*")) {
3274
+ expanded.add(p.slice(0, -2));
3275
+ }
3276
+ }
3277
+ return [...expanded];
3278
+ };
3261
3279
  var ensureOAC = (input) => Effect21.gen(function* () {
3262
3280
  const { name, originType = "s3" } = input;
3263
3281
  const result = yield* cloudfront_exports.make("list_origin_access_controls", {});
@@ -3368,7 +3386,8 @@ var ensureDistribution = (input) => Effect21.gen(function* () {
3368
3386
  const comment = makeDistComment(project, stage, handlerName);
3369
3387
  const s3OriginId = `S3-${bucketName}`;
3370
3388
  const s3OriginDomain = `${bucketName}.s3.${bucketRegion}.amazonaws.com`;
3371
- const hasApiRoutes = apiOriginDomain && routePatterns && routePatterns.length > 0;
3389
+ const expandedRoutePatterns = routePatterns ? expandRoutePatterns(routePatterns) : void 0;
3390
+ const hasApiRoutes = apiOriginDomain && expandedRoutePatterns && expandedRoutePatterns.length > 0;
3372
3391
  const apiOriginId = hasApiRoutes ? `API-${project}-${stage}` : void 0;
3373
3392
  const originsItems = [
3374
3393
  {
@@ -3399,8 +3418,8 @@ var ensureDistribution = (input) => Effect21.gen(function* () {
3399
3418
  const API_METHODS = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"];
3400
3419
  const CACHED_METHODS = ["GET", "HEAD"];
3401
3420
  const cacheBehaviors = hasApiRoutes ? {
3402
- Quantity: routePatterns.length,
3403
- Items: routePatterns.map((pattern) => ({
3421
+ Quantity: expandedRoutePatterns.length,
3422
+ Items: expandedRoutePatterns.map((pattern) => ({
3404
3423
  PathPattern: pattern,
3405
3424
  TargetOriginId: apiOriginId,
3406
3425
  ViewerProtocolPolicy: "redirect-to-https",
@@ -3468,7 +3487,7 @@ var ensureDistribution = (input) => Effect21.gen(function* () {
3468
3487
  const currentLambdaEdgeArn = currentConfig.DefaultCacheBehavior?.LambdaFunctionAssociations?.Items?.[0]?.LambdaFunctionARN;
3469
3488
  const originsMatch = (currentConfig.Origins?.Quantity ?? 0) === originsItems.length;
3470
3489
  const currentBehaviorPatterns = (currentConfig.CacheBehaviors?.Items ?? []).map((b) => b.PathPattern).sort();
3471
- const desiredBehaviorPatterns = (routePatterns ?? []).slice().sort();
3490
+ const desiredBehaviorPatterns = (expandedRoutePatterns ?? []).slice().sort();
3472
3491
  const behaviorsMatch = currentBehaviorPatterns.length === desiredBehaviorPatterns.length && desiredBehaviorPatterns.every((p, i) => currentBehaviorPatterns[i] === p);
3473
3492
  const apiOriginMatch = !hasApiRoutes || currentConfig.Origins?.Items?.some((o) => o.DomainName === apiOriginDomain);
3474
3493
  const needsUpdate = currentOrigin?.DomainName !== s3OriginDomain || currentOrigin?.OriginAccessControlId !== oacId || currentConfig.DefaultRootObject !== index || currentConfig.DefaultCacheBehavior?.CachePolicyId !== CACHING_OPTIMIZED_POLICY_ID || currentConfig.DefaultCacheBehavior?.ResponseHeadersPolicyId !== SECURITY_HEADERS_POLICY_ID || (currentConfig.CustomErrorResponses?.Quantity ?? 0) !== customErrorResponses.Quantity || (currentConfig.DefaultCacheBehavior?.FunctionAssociations?.Quantity ?? 0) !== functionAssociations.Quantity || currentConfig.DefaultCacheBehavior?.FunctionAssociations?.Items?.[0]?.FunctionARN !== (urlRewriteFunctionArn ?? void 0) || (currentConfig.DefaultCacheBehavior?.LambdaFunctionAssociations?.Quantity ?? 0) !== lambdaFunctionAssociations.Quantity || currentLambdaEdgeArn !== (lambdaEdgeArn ?? void 0) || !aliasesMatch || !certMatch || !originsMatch || !behaviorsMatch || !apiOriginMatch;
@@ -3588,7 +3607,7 @@ var ensureDistribution = (input) => Effect21.gen(function* () {
3588
3607
  };
3589
3608
  });
3590
3609
  var ensureSsrDistribution = (input) => Effect21.gen(function* () {
3591
- const { project, stage, handlerName, bucketName, bucketRegion, s3OacId, lambdaOriginDomain, lambdaOacId, assetPatterns, tags, aliases, acmCertificateArn, apiOriginDomain, routePatterns } = input;
3610
+ const { project, stage, handlerName, bucketName, bucketRegion, s3OacId, lambdaOriginDomain, assetPatterns, tags, aliases, acmCertificateArn, apiOriginDomain, routePatterns } = input;
3592
3611
  const comment = makeDistComment(project, stage, handlerName);
3593
3612
  const lambdaOriginId = `Lambda-${project}-${stage}-${handlerName}`;
3594
3613
  const s3OriginId = `S3-${bucketName}`;
@@ -3601,14 +3620,14 @@ var ensureSsrDistribution = (input) => Effect21.gen(function* () {
3601
3620
  } : void 0;
3602
3621
  const ALL_METHODS = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"];
3603
3622
  const CACHED_METHODS = ["GET", "HEAD"];
3604
- const hasApiRoutes = apiOriginDomain && routePatterns && routePatterns.length > 0;
3623
+ const expandedRoutePatterns = routePatterns ? expandRoutePatterns(routePatterns) : void 0;
3624
+ const hasApiRoutes = apiOriginDomain && expandedRoutePatterns && expandedRoutePatterns.length > 0;
3605
3625
  const apiOriginId = hasApiRoutes ? `API-${project}-${stage}` : void 0;
3606
3626
  const originsItems = [
3607
3627
  {
3608
3628
  Id: lambdaOriginId,
3609
3629
  DomainName: lambdaOriginDomain,
3610
3630
  OriginPath: "",
3611
- OriginAccessControlId: lambdaOacId,
3612
3631
  CustomOriginConfig: {
3613
3632
  HTTPPort: 80,
3614
3633
  HTTPSPort: 443,
@@ -3661,7 +3680,7 @@ var ensureSsrDistribution = (input) => Effect21.gen(function* () {
3661
3680
  LambdaFunctionAssociations: { Quantity: 0, Items: [] },
3662
3681
  FieldLevelEncryptionId: ""
3663
3682
  };
3664
- const apiRouteBehaviors = hasApiRoutes ? routePatterns.map((pattern) => ({
3683
+ const apiRouteBehaviors = hasApiRoutes ? expandedRoutePatterns.map((pattern) => ({
3665
3684
  PathPattern: pattern,
3666
3685
  TargetOriginId: apiOriginId,
3667
3686
  ViewerProtocolPolicy: "redirect-to-https",
@@ -4969,8 +4988,6 @@ var deployApp = (input) => Effect29.gen(function* () {
4969
4988
  });
4970
4989
  const s3OacName = `${project}-${stage}-oac`;
4971
4990
  const { oacId: s3OacId } = yield* ensureOAC({ name: s3OacName, originType: "s3" });
4972
- const lambdaOacName = `${project}-${stage}-lambda-oac`;
4973
- const { oacId: lambdaOacId } = yield* ensureOAC({ name: lambdaOacName, originType: "lambda" });
4974
4991
  const assetsDir = path5.resolve(projectDir, config.assets);
4975
4992
  const assetPatterns = detectAssetPatterns(assetsDir);
4976
4993
  yield* Effect29.logDebug(`Detected ${assetPatterns.length} asset pattern(s): ${assetPatterns.join(", ")}`);
@@ -4990,14 +5007,13 @@ var deployApp = (input) => Effect29.gen(function* () {
4990
5007
  bucketRegion: region,
4991
5008
  s3OacId,
4992
5009
  lambdaOriginDomain,
4993
- lambdaOacId,
4994
5010
  assetPatterns,
4995
5011
  tags: makeTags(tagCtx, "cloudfront-distribution"),
4996
5012
  aliases,
4997
5013
  acmCertificateArn,
4998
5014
  ...input.apiOriginDomain && routePatterns.length > 0 ? { apiOriginDomain: input.apiOriginDomain, routePatterns } : {}
4999
5015
  });
5000
- yield* addCloudFrontPermission(lambdaName, distributionArn);
5016
+ yield* addFunctionUrlPublicAccess(lambdaName);
5001
5017
  yield* putBucketPolicyForOAC(bucketName, distributionArn);
5002
5018
  yield* syncFiles({ bucketName, sourceDir: assetsDir });
5003
5019
  yield* invalidateDistribution(distributionId);
@@ -5540,6 +5556,31 @@ var buildBucketNameMap = (bucketHandlers, project, stage) => {
5540
5556
  }
5541
5557
  return map;
5542
5558
  };
5559
+ var validateDeps = (discovered, tableNameMap, bucketNameMap, mailerDomainMap) => {
5560
+ const errors = [];
5561
+ const allGroups = [
5562
+ ...discovered.httpHandlers,
5563
+ ...discovered.apiHandlers,
5564
+ ...discovered.tableHandlers,
5565
+ ...discovered.fifoQueueHandlers,
5566
+ ...discovered.bucketHandlers,
5567
+ ...discovered.staticSiteHandlers,
5568
+ ...discovered.appHandlers,
5569
+ ...discovered.mailerHandlers
5570
+ ];
5571
+ for (const { exports } of allGroups) {
5572
+ for (const fn13 of exports) {
5573
+ for (const key of fn13.depsKeys) {
5574
+ if (!tableNameMap.has(key) && !bucketNameMap.has(key) && !mailerDomainMap.has(key)) {
5575
+ errors.push(
5576
+ `Handler "${fn13.exportName}" depends on "${key}", but no matching table, bucket, or mailer handler was found. Make sure it is exported.`
5577
+ );
5578
+ }
5579
+ }
5580
+ }
5581
+ }
5582
+ return errors;
5583
+ };
5543
5584
  var resolveDeps = (depsKeys, tableNameMap, bucketNameMap, mailerDomainMap) => {
5544
5585
  if (depsKeys.length === 0) return void 0;
5545
5586
  const depsEnv = {};
@@ -5890,6 +5931,14 @@ var deployProject = (input) => Effect35.gen(function* () {
5890
5931
  const tableNameMap = buildTableNameMap(tableHandlers, input.project, stage);
5891
5932
  const bucketNameMap = buildBucketNameMap(bucketHandlers, input.project, stage);
5892
5933
  const mailerDomainMap = buildMailerDomainMap(mailerHandlers);
5934
+ const depsErrors = validateDeps(discovered, tableNameMap, bucketNameMap, mailerDomainMap);
5935
+ if (depsErrors.length > 0) {
5936
+ yield* Console2.log("");
5937
+ for (const err of depsErrors) {
5938
+ yield* Console2.log(` ${c.red("\u2717")} ${err}`);
5939
+ }
5940
+ return yield* Effect35.fail(new Error("Unresolved deps \u2014 aborting deploy"));
5941
+ }
5893
5942
  const { layerArn, layerVersion, layerStatus, external } = yield* prepareLayer({
5894
5943
  project: input.project,
5895
5944
  stage,
@@ -6525,6 +6574,15 @@ API: ${c.cyan(apiUrl)}`);
6525
6574
  if (counts.orphaned > 0) parts.push(c.red(`${counts.orphaned} orphaned`));
6526
6575
  yield* Console4.log(`
6527
6576
  Total: ${parts.join(", ")}`);
6577
+ const depWarnings = yield* checkDependencyWarnings(projectDir).pipe(
6578
+ Effect39.catchAll(() => Effect39.succeed([]))
6579
+ );
6580
+ if (depWarnings.length > 0) {
6581
+ yield* Console4.log("");
6582
+ for (const w of depWarnings) {
6583
+ yield* Console4.log(c.yellow(` \u26A0 ${w}`));
6584
+ }
6585
+ }
6528
6586
  }).pipe(
6529
6587
  Effect39.provide(clientsLayer),
6530
6588
  Logger2.withMinimumLogLevel(logLevel)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effortless-aws/cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "CLI and deploy tooling for effortless-aws",