@awsless/cli 0.0.43 → 0.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -1666,6 +1666,7 @@ var RouterDefaultSchema = z21.record(
1666
1666
  z21.object({
1667
1667
  domain: ResourceIdSchema.describe("The domain id to link your Router.").optional(),
1668
1668
  subDomain: z21.string().optional(),
1669
+ redirectWww: z21.boolean().default(false).describe("Redirect all www subdomain requests to your root domain."),
1669
1670
  waf: WafSettingsSchema.optional(),
1670
1671
  geoRestrictions: z21.array(z21.string().length(2).toUpperCase()).default([]).describe("Specifies a blacklist of countries that should be blocked."),
1671
1672
  errors: z21.object({
@@ -1754,6 +1755,21 @@ var RouterDefaultSchema = z21.record(
1754
1755
  }).optional().describe(
1755
1756
  "Specifies the cookies, headers, and query values that CloudFront includes in the cache key."
1756
1757
  )
1758
+ }).superRefine((props, ctx) => {
1759
+ if (props.redirectWww && !props.domain) {
1760
+ ctx.addIssue({
1761
+ code: z21.ZodIssueCode.custom,
1762
+ path: ["redirectWww"],
1763
+ message: "The redirectWww option requires a domain to be set."
1764
+ });
1765
+ }
1766
+ if (props.redirectWww && props.subDomain) {
1767
+ ctx.addIssue({
1768
+ code: z21.ZodIssueCode.custom,
1769
+ path: ["redirectWww"],
1770
+ message: `The redirectWww option can't be combined with a subDomain, because the domain certificate only covers single level subdomains.`
1771
+ });
1772
+ }
1757
1773
  })
1758
1774
  ).optional().describe(`Define the global Router. Backed by AWS CloudFront.`);
1759
1775
 
@@ -9165,6 +9181,7 @@ import { camelCase as camelCase9, constantCase as constantCase16, kebabCase as k
9165
9181
  var getViewerRequestFunctionCode = (props) => {
9166
9182
  return CODE([
9167
9183
  props.blockDirectAccess ? BLOCK_DIRECT_ACCESS_TO_CLOUDFRONT : "",
9184
+ props.redirectWww ? REDIRECT_WWW : "",
9168
9185
  props.passwordAuth ?? props.basicAuth ? AUTH_WRAPPER(
9169
9186
  [
9170
9187
  //
@@ -9181,6 +9198,38 @@ if (headers.host && headers.host.value.includes('cloudfront.net')) {
9181
9198
  statusDescription: 'Forbidden'
9182
9199
  };
9183
9200
  }`;
9201
+ var REDIRECT_WWW = `
9202
+ if (headers.host && headers.host.value.startsWith('www.')) {
9203
+ let location = 'https://' + headers.host.value.slice(4) + request.uri;
9204
+ const query = [];
9205
+
9206
+ for(const key in request.querystring) {
9207
+ const item = request.querystring[key];
9208
+
9209
+ if(item.multiValue) {
9210
+ for(const i in item.multiValue) {
9211
+ query.push(key + '=' + item.multiValue[i].value);
9212
+ }
9213
+ } else if(item.value === '') {
9214
+ query.push(key);
9215
+ } else {
9216
+ query.push(key + '=' + item.value);
9217
+ }
9218
+ }
9219
+
9220
+ if(query.length > 0) {
9221
+ location += '?' + query.join('&');
9222
+ }
9223
+
9224
+ return {
9225
+ statusCode: 301,
9226
+ statusDescription: 'Moved Permanently',
9227
+ headers: {
9228
+ 'location': { value: location },
9229
+ 'strict-transport-security': { value: 'max-age=31536000; includeSubdomains; preload' }
9230
+ }
9231
+ };
9232
+ }`;
9184
9233
  var BASIC_AUTH_CHECK = (username, password) => `
9185
9234
  authMethods.push('Basic realm="Protected"');
9186
9235
 
@@ -9286,14 +9335,37 @@ async function findRoute(path, method) {
9286
9335
  const keys = getPossibleRouteKeys(path);
9287
9336
 
9288
9337
  for(const i in keys) {
9338
+ const key = keys[i];
9339
+ let value;
9340
+
9289
9341
  try {
9290
- const value = await store.get(keys[i], { format: 'json' });
9291
- const result = matchRoute(value, path, method);
9342
+ value = await store.get(key, { format: 'json' });
9343
+ } catch (e) {
9344
+ continue;
9345
+ }
9292
9346
 
9293
- if(result) {
9294
- return result;
9347
+ // Route lists that are too big for a single key value pair
9348
+ // are sharded over multiple entries behind a route index.
9349
+ if(value && value.list) {
9350
+ for(let n = 0; n < value.list; n++) {
9351
+ try {
9352
+ const route = await store.get(key + '#' + n, { format: 'json' });
9353
+ const result = matchRoute(route, path, method);
9354
+
9355
+ if(result) {
9356
+ return result;
9357
+ }
9358
+ } catch (e) {}
9295
9359
  }
9296
- } catch (e) {}
9360
+
9361
+ continue;
9362
+ }
9363
+
9364
+ const result = matchRoute(value, path, method);
9365
+
9366
+ if(result) {
9367
+ return result;
9368
+ }
9297
9369
  }
9298
9370
  }
9299
9371
 
@@ -9447,6 +9519,32 @@ async function handler(event) {
9447
9519
 
9448
9520
  // src/feature/router/index.ts
9449
9521
  import { createHash as createHash8 } from "crypto";
9522
+
9523
+ // src/feature/router/route.ts
9524
+ var MAX_VALUE_SIZE = 1e3;
9525
+ var createRouteStoreEntries = (routes) => {
9526
+ const entries = [];
9527
+ for (const [key, value] of Object.entries(routes)) {
9528
+ const json = JSON.stringify(value);
9529
+ if (Array.isArray(value) && Buffer.byteLength(json, "utf8") > MAX_VALUE_SIZE) {
9530
+ entries.push({
9531
+ key,
9532
+ value: JSON.stringify({ list: value.length })
9533
+ });
9534
+ value.forEach((route, index) => {
9535
+ entries.push({
9536
+ key: `${key}#${index}`,
9537
+ value: JSON.stringify(route)
9538
+ });
9539
+ });
9540
+ } else {
9541
+ entries.push({ key, value: json });
9542
+ }
9543
+ }
9544
+ return entries;
9545
+ };
9546
+
9547
+ // src/feature/router/index.ts
9450
9548
  var routerFeature = defineFeature({
9451
9549
  name: "router",
9452
9550
  onApp(ctx) {
@@ -9476,9 +9574,7 @@ var routerFeature = defineFeature({
9476
9574
  {
9477
9575
  kvsArn: routeStore.arn,
9478
9576
  keys: $resolve([routes], (routes2) => {
9479
- return Object.entries(routes2).map(([key, value]) => {
9480
- return { key, value: JSON.stringify(value) };
9481
- });
9577
+ return createRouteStoreEntries(routes2);
9482
9578
  })
9483
9579
  },
9484
9580
  {
@@ -9584,6 +9680,7 @@ var routerFeature = defineFeature({
9584
9680
  keyValueStoreAssociations: [routeStore.arn],
9585
9681
  code: getViewerRequestFunctionCode({
9586
9682
  blockDirectAccess: !!props.domain,
9683
+ redirectWww: !!props.domain && props.redirectWww,
9587
9684
  basicAuth: props.basicAuth,
9588
9685
  passwordAuth: props.passwordAuth
9589
9686
  })
@@ -9813,6 +9910,7 @@ var routerFeature = defineFeature({
9813
9910
  });
9814
9911
  if (props.domain) {
9815
9912
  const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
9913
+ const wwwDomainName = props.redirectWww ? `www.${domainName}` : void 0;
9816
9914
  const certificateArn = ctx.shared.entry("domain", `global-certificate-arn`, props.domain);
9817
9915
  const zoneId = ctx.shared.entry("domain", "zone-id", props.domain);
9818
9916
  const connectionGroup = new aws32.cloudfront.ConnectionGroup(group, "connection-group", {
@@ -9823,7 +9921,11 @@ var routerFeature = defineFeature({
9823
9921
  enabled: true,
9824
9922
  distributionId: distribution.id,
9825
9923
  connectionGroupId: connectionGroup.id,
9826
- domain: [{ domain: domainName }],
9924
+ domain: [
9925
+ //
9926
+ { domain: domainName },
9927
+ ...wwwDomainName ? [{ domain: wwwDomainName }] : []
9928
+ ],
9827
9929
  customizations: [{ certificate: [{ arn: certificateArn }] }]
9828
9930
  });
9829
9931
  new aws32.route53.Record(group, `record`, {
@@ -9836,6 +9938,18 @@ var routerFeature = defineFeature({
9836
9938
  evaluateTargetHealth: false
9837
9939
  }
9838
9940
  });
9941
+ if (wwwDomainName) {
9942
+ new aws32.route53.Record(group, `www-record`, {
9943
+ zoneId,
9944
+ type: "A",
9945
+ name: wwwDomainName,
9946
+ alias: {
9947
+ name: connectionGroup.routingEndpoint,
9948
+ zoneId: "Z2FDTNDATAQYW2",
9949
+ evaluateTargetHealth: false
9950
+ }
9951
+ });
9952
+ }
9839
9953
  ctx.bind(`ROUTER_${constantCase16(id)}_ENDPOINT`, domainName);
9840
9954
  }
9841
9955
  }
@@ -724,6 +724,7 @@ var RouterDefaultSchema = z17.record(
724
724
  z17.object({
725
725
  domain: ResourceIdSchema.describe("The domain id to link your Router.").optional(),
726
726
  subDomain: z17.string().optional(),
727
+ redirectWww: z17.boolean().default(false).describe("Redirect all www subdomain requests to your root domain."),
727
728
  waf: WafSettingsSchema.optional(),
728
729
  geoRestrictions: z17.array(z17.string().length(2).toUpperCase()).default([]).describe("Specifies a blacklist of countries that should be blocked."),
729
730
  errors: z17.object({
@@ -812,6 +813,21 @@ var RouterDefaultSchema = z17.record(
812
813
  }).optional().describe(
813
814
  "Specifies the cookies, headers, and query values that CloudFront includes in the cache key."
814
815
  )
816
+ }).superRefine((props, ctx) => {
817
+ if (props.redirectWww && !props.domain) {
818
+ ctx.addIssue({
819
+ code: z17.ZodIssueCode.custom,
820
+ path: ["redirectWww"],
821
+ message: "The redirectWww option requires a domain to be set."
822
+ });
823
+ }
824
+ if (props.redirectWww && props.subDomain) {
825
+ ctx.addIssue({
826
+ code: z17.ZodIssueCode.custom,
827
+ path: ["redirectWww"],
828
+ message: `The redirectWww option can't be combined with a subDomain, because the domain certificate only covers single level subdomains.`
829
+ });
830
+ }
815
831
  })
816
832
  ).optional().describe(`Define the global Router. Backed by AWS CloudFront.`);
817
833
 
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awsless/cli",
3
- "version": "0.0.43",
3
+ "version": "0.0.45",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -89,23 +89,23 @@
89
89
  "zod": "^3.24.2",
90
90
  "zod-to-json-schema": "^3.24.3",
91
91
  "@awsless/big-float": "^0.1.7",
92
- "@awsless/duration": "^0.0.4",
93
92
  "@awsless/dynamodb": "^0.3.22",
94
- "@awsless/cloudwatch": "^0.0.1",
93
+ "@awsless/lambda": "^0.0.44",
94
+ "@awsless/redis": "^0.1.13",
95
+ "@awsless/clui": "^0.0.8",
96
+ "@awsless/duration": "^0.0.4",
97
+ "@awsless/iot": "^0.0.5",
98
+ "@awsless/s3": "^0.0.21",
95
99
  "@awsless/json": "^0.0.11",
96
100
  "@awsless/size": "^0.0.2",
97
- "@awsless/s3": "^0.0.21",
98
- "@awsless/scheduler": "^0.0.4",
99
- "@awsless/lambda": "^0.0.44",
100
101
  "@awsless/sns": "^0.0.10",
101
- "@awsless/clui": "^0.0.8",
102
102
  "@awsless/validate": "^0.1.7",
103
103
  "@awsless/weak-cache": "^0.0.1",
104
- "@awsless/redis": "^0.1.13",
105
- "awsless": "^0.0.14",
106
- "@awsless/iot": "^0.0.5",
104
+ "@awsless/cloudwatch": "^0.0.1",
105
+ "@awsless/scheduler": "^0.0.4",
107
106
  "@awsless/ts-file-cache": "^0.0.16",
108
- "@awsless/sqs": "^0.0.24"
107
+ "@awsless/sqs": "^0.0.24",
108
+ "awsless": "^0.0.14"
109
109
  },
110
110
  "devDependencies": {
111
111
  "@hono/node-server": "1.19.9",