@effortless-aws/cli 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli/index.js +88 -6
  2. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -2660,6 +2660,14 @@ var DEV_ONLY_PREFIXES = [
2660
2660
  "@vitest/",
2661
2661
  "@jest/"
2662
2662
  ];
2663
+ var extractExportPaths = (value) => {
2664
+ if (typeof value === "string") return [value];
2665
+ if (typeof value === "object" && value !== null) {
2666
+ return Object.values(value).flatMap(extractExportPaths);
2667
+ }
2668
+ return [];
2669
+ };
2670
+ var isRawTypeScript = (p) => /\.(?:ts|tsx|mts|cts)$/.test(p) && !p.endsWith(".d.ts") && !p.endsWith(".d.mts") && !p.endsWith(".d.cts");
2663
2671
  var checkDependencyWarnings = (projectDir) => Effect18.gen(function* () {
2664
2672
  const pkgPath = path.join(projectDir, "package.json");
2665
2673
  const content = yield* Effect18.tryPromise({
@@ -2684,6 +2692,26 @@ var checkDependencyWarnings = (projectDir) => Effect18.gen(function* () {
2684
2692
  `"dependencies" is empty but "devDependencies" has ${devDeps.length} package(s). Runtime packages must be in "dependencies" to be included in the Lambda layer.`
2685
2693
  );
2686
2694
  }
2695
+ for (const dep of deps) {
2696
+ const depPath = getPackageRealPath(projectDir, dep);
2697
+ if (!depPath) continue;
2698
+ const depPkgPath = path.join(depPath, "package.json");
2699
+ if (!fsSync.existsSync(depPkgPath)) continue;
2700
+ try {
2701
+ const depPkg = JSON.parse(fsSync.readFileSync(depPkgPath, "utf-8"));
2702
+ const entryPoints = [];
2703
+ if (typeof depPkg.main === "string") entryPoints.push(depPkg.main);
2704
+ if (typeof depPkg.module === "string") entryPoints.push(depPkg.module);
2705
+ entryPoints.push(...extractExportPaths(depPkg.exports));
2706
+ const tsEntries = [...new Set(entryPoints.filter(isRawTypeScript))];
2707
+ if (tsEntries.length > 0) {
2708
+ warnings.push(
2709
+ `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.`
2710
+ );
2711
+ }
2712
+ } catch {
2713
+ }
2714
+ }
2687
2715
  return warnings;
2688
2716
  });
2689
2717
  var getPackageRealPath = (projectDir, pkgName) => {
@@ -3258,6 +3286,16 @@ var CACHING_OPTIMIZED_POLICY_ID = "658327ea-f89d-4fab-a63d-7e88639e58f6";
3258
3286
  var CACHING_DISABLED_POLICY_ID = "4135ea2d-6df8-44a3-9df3-4b5a84be39ad";
3259
3287
  var ALL_VIEWER_EXCEPT_HOST_HEADER_POLICY_ID = "b689b0a8-53d0-40ab-baf2-68738e2966ac";
3260
3288
  var SECURITY_HEADERS_POLICY_ID = "67f7725c-6f97-4210-82d7-5512b31e9d03";
3289
+ var expandRoutePatterns = (patterns) => {
3290
+ const expanded = /* @__PURE__ */ new Set();
3291
+ for (const p of patterns) {
3292
+ expanded.add(p);
3293
+ if (p.endsWith("/*")) {
3294
+ expanded.add(p.slice(0, -2));
3295
+ }
3296
+ }
3297
+ return [...expanded];
3298
+ };
3261
3299
  var ensureOAC = (input) => Effect21.gen(function* () {
3262
3300
  const { name, originType = "s3" } = input;
3263
3301
  const result = yield* cloudfront_exports.make("list_origin_access_controls", {});
@@ -3368,7 +3406,8 @@ var ensureDistribution = (input) => Effect21.gen(function* () {
3368
3406
  const comment = makeDistComment(project, stage, handlerName);
3369
3407
  const s3OriginId = `S3-${bucketName}`;
3370
3408
  const s3OriginDomain = `${bucketName}.s3.${bucketRegion}.amazonaws.com`;
3371
- const hasApiRoutes = apiOriginDomain && routePatterns && routePatterns.length > 0;
3409
+ const expandedRoutePatterns = routePatterns ? expandRoutePatterns(routePatterns) : void 0;
3410
+ const hasApiRoutes = apiOriginDomain && expandedRoutePatterns && expandedRoutePatterns.length > 0;
3372
3411
  const apiOriginId = hasApiRoutes ? `API-${project}-${stage}` : void 0;
3373
3412
  const originsItems = [
3374
3413
  {
@@ -3399,8 +3438,8 @@ var ensureDistribution = (input) => Effect21.gen(function* () {
3399
3438
  const API_METHODS = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"];
3400
3439
  const CACHED_METHODS = ["GET", "HEAD"];
3401
3440
  const cacheBehaviors = hasApiRoutes ? {
3402
- Quantity: routePatterns.length,
3403
- Items: routePatterns.map((pattern) => ({
3441
+ Quantity: expandedRoutePatterns.length,
3442
+ Items: expandedRoutePatterns.map((pattern) => ({
3404
3443
  PathPattern: pattern,
3405
3444
  TargetOriginId: apiOriginId,
3406
3445
  ViewerProtocolPolicy: "redirect-to-https",
@@ -3468,7 +3507,7 @@ var ensureDistribution = (input) => Effect21.gen(function* () {
3468
3507
  const currentLambdaEdgeArn = currentConfig.DefaultCacheBehavior?.LambdaFunctionAssociations?.Items?.[0]?.LambdaFunctionARN;
3469
3508
  const originsMatch = (currentConfig.Origins?.Quantity ?? 0) === originsItems.length;
3470
3509
  const currentBehaviorPatterns = (currentConfig.CacheBehaviors?.Items ?? []).map((b) => b.PathPattern).sort();
3471
- const desiredBehaviorPatterns = (routePatterns ?? []).slice().sort();
3510
+ const desiredBehaviorPatterns = (expandedRoutePatterns ?? []).slice().sort();
3472
3511
  const behaviorsMatch = currentBehaviorPatterns.length === desiredBehaviorPatterns.length && desiredBehaviorPatterns.every((p, i) => currentBehaviorPatterns[i] === p);
3473
3512
  const apiOriginMatch = !hasApiRoutes || currentConfig.Origins?.Items?.some((o) => o.DomainName === apiOriginDomain);
3474
3513
  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;
@@ -3601,7 +3640,8 @@ var ensureSsrDistribution = (input) => Effect21.gen(function* () {
3601
3640
  } : void 0;
3602
3641
  const ALL_METHODS = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"];
3603
3642
  const CACHED_METHODS = ["GET", "HEAD"];
3604
- const hasApiRoutes = apiOriginDomain && routePatterns && routePatterns.length > 0;
3643
+ const expandedRoutePatterns = routePatterns ? expandRoutePatterns(routePatterns) : void 0;
3644
+ const hasApiRoutes = apiOriginDomain && expandedRoutePatterns && expandedRoutePatterns.length > 0;
3605
3645
  const apiOriginId = hasApiRoutes ? `API-${project}-${stage}` : void 0;
3606
3646
  const originsItems = [
3607
3647
  {
@@ -3661,7 +3701,7 @@ var ensureSsrDistribution = (input) => Effect21.gen(function* () {
3661
3701
  LambdaFunctionAssociations: { Quantity: 0, Items: [] },
3662
3702
  FieldLevelEncryptionId: ""
3663
3703
  };
3664
- const apiRouteBehaviors = hasApiRoutes ? routePatterns.map((pattern) => ({
3704
+ const apiRouteBehaviors = hasApiRoutes ? expandedRoutePatterns.map((pattern) => ({
3665
3705
  PathPattern: pattern,
3666
3706
  TargetOriginId: apiOriginId,
3667
3707
  ViewerProtocolPolicy: "redirect-to-https",
@@ -5540,6 +5580,31 @@ var buildBucketNameMap = (bucketHandlers, project, stage) => {
5540
5580
  }
5541
5581
  return map;
5542
5582
  };
5583
+ var validateDeps = (discovered, tableNameMap, bucketNameMap, mailerDomainMap) => {
5584
+ const errors = [];
5585
+ const allGroups = [
5586
+ ...discovered.httpHandlers,
5587
+ ...discovered.apiHandlers,
5588
+ ...discovered.tableHandlers,
5589
+ ...discovered.fifoQueueHandlers,
5590
+ ...discovered.bucketHandlers,
5591
+ ...discovered.staticSiteHandlers,
5592
+ ...discovered.appHandlers,
5593
+ ...discovered.mailerHandlers
5594
+ ];
5595
+ for (const { exports } of allGroups) {
5596
+ for (const fn13 of exports) {
5597
+ for (const key of fn13.depsKeys) {
5598
+ if (!tableNameMap.has(key) && !bucketNameMap.has(key) && !mailerDomainMap.has(key)) {
5599
+ errors.push(
5600
+ `Handler "${fn13.exportName}" depends on "${key}", but no matching table, bucket, or mailer handler was found. Make sure it is exported.`
5601
+ );
5602
+ }
5603
+ }
5604
+ }
5605
+ }
5606
+ return errors;
5607
+ };
5543
5608
  var resolveDeps = (depsKeys, tableNameMap, bucketNameMap, mailerDomainMap) => {
5544
5609
  if (depsKeys.length === 0) return void 0;
5545
5610
  const depsEnv = {};
@@ -5890,6 +5955,14 @@ var deployProject = (input) => Effect35.gen(function* () {
5890
5955
  const tableNameMap = buildTableNameMap(tableHandlers, input.project, stage);
5891
5956
  const bucketNameMap = buildBucketNameMap(bucketHandlers, input.project, stage);
5892
5957
  const mailerDomainMap = buildMailerDomainMap(mailerHandlers);
5958
+ const depsErrors = validateDeps(discovered, tableNameMap, bucketNameMap, mailerDomainMap);
5959
+ if (depsErrors.length > 0) {
5960
+ yield* Console2.log("");
5961
+ for (const err of depsErrors) {
5962
+ yield* Console2.log(` ${c.red("\u2717")} ${err}`);
5963
+ }
5964
+ return yield* Effect35.fail(new Error("Unresolved deps \u2014 aborting deploy"));
5965
+ }
5893
5966
  const { layerArn, layerVersion, layerStatus, external } = yield* prepareLayer({
5894
5967
  project: input.project,
5895
5968
  stage,
@@ -6525,6 +6598,15 @@ API: ${c.cyan(apiUrl)}`);
6525
6598
  if (counts.orphaned > 0) parts.push(c.red(`${counts.orphaned} orphaned`));
6526
6599
  yield* Console4.log(`
6527
6600
  Total: ${parts.join(", ")}`);
6601
+ const depWarnings = yield* checkDependencyWarnings(projectDir).pipe(
6602
+ Effect39.catchAll(() => Effect39.succeed([]))
6603
+ );
6604
+ if (depWarnings.length > 0) {
6605
+ yield* Console4.log("");
6606
+ for (const w of depWarnings) {
6607
+ yield* Console4.log(c.yellow(` \u26A0 ${w}`));
6608
+ }
6609
+ }
6528
6610
  }).pipe(
6529
6611
  Effect39.provide(clientsLayer),
6530
6612
  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.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "CLI and deploy tooling for effortless-aws",