@digitraffic/common 2023.4.4-1 → 2023.5.9-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,8 +10,9 @@ import { IModel } from "aws-cdk-lib/aws-apigateway/lib/model";
10
10
  * non-textual content.
11
11
  *
12
12
  * If fileName is set, then Content-Disposition-header will be set to use it
13
+ * If timestamp is set, then ETag & Last-Modified headers will be set
13
14
  */
14
- export declare const RESPONSE_DEFAULT_LAMBDA = "#set($inputRoot = $input.path('$'))\n$util.base64Decode($inputRoot.body)\n#if ($inputRoot.status != 200)\n#set ($context.responseOverride.status = $inputRoot.status)\n#set ($context.responseOverride.header.Content-Type = 'text/plain')\n#end\n#set ($context.responseOverride.header.Access-Control-Allow-Origin = '*')\n#if (\"$!inputRoot.fileName\" != \"\")\n#set ($disposition = 'attachment; filename=\"FN\"')\n#set ($context.responseOverride.header.Content-Disposition = $disposition.replaceAll('FN', $inputRoot.fileName))\n#end\n";
15
+ export declare const RESPONSE_DEFAULT_LAMBDA = "#set($inputRoot = $input.path('$'))\n#if ($inputRoot.status != 200)\n#set ($context.responseOverride.status = $inputRoot.status)\n#set ($context.responseOverride.header.Content-Type = 'text/plain')\n#end\n#set ($context.responseOverride.header.Access-Control-Allow-Origin = '*')\n#if (\"$!inputRoot.timestamp\" != \"\")\n#set ($context.responseOverride.header.ETag = $inputRoot.timestamp)\n#set ($context.responseOverride.header.Last-Modified = $inputRoot.timestamp)\n#end\n#if (\"$!inputRoot.fileName\" != \"\")\n#set ($disposition = 'attachment; filename=\"FN\"')\n#set ($context.responseOverride.header.Content-Disposition = $disposition.replaceAll('FN', $inputRoot.fileName))\n#end\n$util.base64Decode($inputRoot.body)";
15
16
  /**
16
17
  * Use this for deprecated integrations.
17
18
  * Will add HTTP headers Deprecation and Sunset to response.
@@ -13,19 +13,23 @@ const date_utils_1 = require("../../../utils/date-utils");
13
13
  * non-textual content.
14
14
  *
15
15
  * If fileName is set, then Content-Disposition-header will be set to use it
16
+ * If timestamp is set, then ETag & Last-Modified headers will be set
16
17
  */
17
18
  exports.RESPONSE_DEFAULT_LAMBDA = `#set($inputRoot = $input.path('$'))
18
- $util.base64Decode($inputRoot.body)
19
19
  #if ($inputRoot.status != 200)
20
20
  #set ($context.responseOverride.status = $inputRoot.status)
21
21
  #set ($context.responseOverride.header.Content-Type = 'text/plain')
22
22
  #end
23
23
  #set ($context.responseOverride.header.Access-Control-Allow-Origin = '*')
24
+ #if ("$!inputRoot.timestamp" != "")
25
+ #set ($context.responseOverride.header.ETag = $inputRoot.timestamp)
26
+ #set ($context.responseOverride.header.Last-Modified = $inputRoot.timestamp)
27
+ #end
24
28
  #if ("$!inputRoot.fileName" != "")
25
29
  #set ($disposition = 'attachment; filename="FN"')
26
30
  #set ($context.responseOverride.header.Content-Disposition = $disposition.replaceAll('FN', $inputRoot.fileName))
27
31
  #end
28
- `;
32
+ $util.base64Decode($inputRoot.body)`;
29
33
  /**
30
34
  * Use this for deprecated integrations.
31
35
  * Will add HTTP headers Deprecation and Sunset to response.
@@ -12,8 +12,6 @@ export interface DbConfiguration {
12
12
  readonly instances: number;
13
13
  readonly customParameterGroup: boolean;
14
14
  readonly securityGroupId: string;
15
- readonly superuserName: string;
16
- readonly superuserPassword: string;
17
15
  readonly proxy: {
18
16
  readonly name?: string;
19
17
  readonly securityGroupId: string;
@@ -4,6 +4,7 @@ exports.DbStack = void 0;
4
4
  const aws_cdk_lib_1 = require("aws-cdk-lib");
5
5
  const aws_ec2_1 = require("aws-cdk-lib/aws-ec2");
6
6
  const aws_rds_1 = require("aws-cdk-lib/aws-rds");
7
+ const aws_secretsmanager_1 = require("aws-cdk-lib/aws-secretsmanager");
7
8
  const import_util_1 = require("../import-util");
8
9
  /**
9
10
  * Stack that creates DatabaseCluster.
@@ -43,6 +44,7 @@ class DbStack extends aws_cdk_lib_1.Stack {
43
44
  : aws_rds_1.ParameterGroup.fromParameterGroupName(this, "ParameterGroup", `default.aurora-postgresql${configuration.dbVersion.auroraPostgresMajorVersion}`);
44
45
  }
45
46
  createClusterParameters(configuration, instanceName, vpc, securityGroup, parameterGroup) {
47
+ const secret = aws_secretsmanager_1.Secret.fromSecretCompleteArn(this, "DBSecret", configuration.secretArn);
46
48
  return {
47
49
  engine: aws_rds_1.DatabaseClusterEngine.auroraPostgres({
48
50
  version: configuration.dbVersion,
@@ -71,7 +73,7 @@ class DbStack extends aws_cdk_lib_1.Stack {
71
73
  instanceType: configuration.dbInstanceType,
72
74
  parameterGroup,
73
75
  },
74
- credentials: aws_rds_1.Credentials.fromPassword(configuration.superuserName, aws_cdk_lib_1.SecretValue.unsafePlainText(configuration.superuserPassword)),
76
+ credentials: aws_rds_1.Credentials.fromPassword(secret.secretValueFromJson("db.superuser").unsafeUnwrap(), secret.secretValueFromJson("db.superuser.password")),
75
77
  parameterGroup,
76
78
  };
77
79
  }
@@ -93,7 +95,7 @@ class DbStack extends aws_cdk_lib_1.Stack {
93
95
  throw new Error("Couldn't pull CfnDBInstances from the L1 constructs!");
94
96
  }
95
97
  cfnInstances.forEach((cfnInstance) => delete cfnInstance.engineVersion);
96
- cluster.node.addDependency(parameterGroup, "Create ParameterGroup before DatabaseCluster");
98
+ cluster.node.addDependency(parameterGroup);
97
99
  return cluster;
98
100
  }
99
101
  }
@@ -19,7 +19,9 @@ class NetworkStack extends aws_cdk_lib_1.Stack {
19
19
  createVpc(configuration) {
20
20
  return new aws_ec2_1.Vpc(this, "DigitrafficVPC", {
21
21
  vpcName: configuration.vpcName,
22
- availabilityZones: ["eu-west-1a", "eu-west-1b"],
22
+ availabilityZones: aws_cdk_lib_1.Stack.of(this)
23
+ .availabilityZones.sort()
24
+ .slice(0, 2),
23
25
  enableDnsHostnames: true,
24
26
  enableDnsSupport: true,
25
27
  ipAddresses: aws_ec2_1.IpAddresses.cidr(configuration.cidr),
@@ -2,7 +2,9 @@ export declare class LambdaResponse {
2
2
  readonly status: number;
3
3
  readonly body: string;
4
4
  readonly fileName?: string;
5
- constructor(status: number, body: string, fileName?: string);
5
+ readonly timestamp?: string;
6
+ constructor(status: number, body: string, fileName?: string, timestamp?: Date);
7
+ withTimestamp(timestamp: Date): LambdaResponse;
6
8
  /**
7
9
  * Create LambdaResponse for HTTP 200 from json.
8
10
  */
@@ -31,4 +33,6 @@ export declare class LambdaResponse {
31
33
  * Create LambdaResponse for HTTP 501
32
34
  */
33
35
  static notImplemented(): LambdaResponse;
36
+ private static createForString;
37
+ private static createForBase64;
34
38
  }
@@ -2,10 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LambdaResponse = void 0;
4
4
  class LambdaResponse {
5
- constructor(status, body, fileName) {
5
+ constructor(status, body, fileName, timestamp) {
6
6
  this.status = status;
7
7
  this.body = body;
8
8
  this.fileName = fileName;
9
+ this.timestamp = timestamp?.toUTCString();
10
+ }
11
+ withTimestamp(timestamp) {
12
+ return new LambdaResponse(this.status, this.body, this.fileName, timestamp);
9
13
  }
10
14
  /**
11
15
  * Create LambdaResponse for HTTP 200 from json.
@@ -23,41 +27,41 @@ class LambdaResponse {
23
27
  * Create LambdaResponse for HTTP 200 from base64-encoded data.
24
28
  */
25
29
  static okBinary(base64, fileName) {
26
- return createForBase64(200, base64, fileName);
30
+ return this.createForBase64(200, base64, fileName);
27
31
  }
28
32
  /**
29
33
  * Create LambdaResponse for HTTP 400
30
34
  */
31
35
  static badRequest(body) {
32
- return createForString(400, body);
36
+ return this.createForString(400, body);
33
37
  }
34
38
  /**
35
39
  * Create LambdaResponse for HTTP 404
36
40
  */
37
41
  static notFound() {
38
- return createForString(404, "Not found");
42
+ return this.createForString(404, "Not found");
39
43
  }
40
44
  /**
41
45
  * Create LambdaResponse for HTTP 500
42
46
  */
43
47
  static internalError() {
44
- return createForString(500, "Internal error");
48
+ return this.createForString(500, "Internal error");
45
49
  }
46
50
  /**
47
51
  * Create LambdaResponse for HTTP 501
48
52
  */
49
53
  static notImplemented() {
50
- return createForString(501, "Not implemented");
54
+ return this.createForString(501, "Not implemented");
55
+ }
56
+ static createForString(status, body, fileName) {
57
+ return this.createForBase64(status, toBase64(body), fileName);
58
+ }
59
+ static createForBase64(status, base64Body, fileName) {
60
+ return new LambdaResponse(status, base64Body, fileName);
51
61
  }
52
62
  }
53
63
  exports.LambdaResponse = LambdaResponse;
54
64
  function toBase64(body) {
55
65
  return Buffer.from(body).toString("base64");
56
66
  }
57
- function createForString(status, body, fileName) {
58
- return createForBase64(status, toBase64(body), fileName);
59
- }
60
- function createForBase64(status, base64Body, fileName) {
61
- return new LambdaResponse(status, base64Body, fileName);
62
- }
63
67
  //# sourceMappingURL=lambda-response.js.map
@@ -1,19 +1,20 @@
1
+ import { ValueOf } from "./util-types";
1
2
  /**
2
3
  * Adds `null` as an accepted type to all properties in given type.
3
4
  */
4
5
  export type Nullable<Obj> = {
5
6
  [Key in keyof Obj]: Obj[Key] | null;
6
7
  };
7
- type RequiredKeys<Obj> = {
8
+ type RequiredKeys<Obj> = ValueOf<{
8
9
  [Key in keyof Obj]-?: object extends {
9
10
  [K in Key]: Obj[Key];
10
11
  } ? never : Key;
11
- }[keyof Obj];
12
- type OptionalKeys<Obj> = {
12
+ }>;
13
+ type OptionalKeys<Obj> = ValueOf<{
13
14
  [Key in keyof Obj]-?: object extends {
14
15
  [K in Key]: Obj[Key];
15
16
  } ? Key : never;
16
- }[keyof Obj];
17
+ }>;
17
18
  type RequiredProperties<Obj> = Pick<Obj, RequiredKeys<Obj>>;
18
19
  type OptionalProperties<Obj> = Pick<Obj, OptionalKeys<Obj>>;
19
20
  /**
@@ -0,0 +1 @@
1
+ export type ValueOf<Obj> = Obj[keyof Obj];
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=util-types.js.map
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@digitraffic/common",
3
- "version": "2023.4.4-1",
3
+ "version": "2023.5.9-1",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/tmfg/digitraffic-common.git"
8
8
  },
9
9
  "engines": {
10
- "node": ">=14 <17"
10
+ "node": ">=14 <17 || >=18 <19"
11
11
  },
12
12
  "license": "EUPL-1.2",
13
13
  "private": false,
@@ -17,53 +17,54 @@
17
17
  "src/**/*.ts"
18
18
  ],
19
19
  "peerDependencies": {
20
- "@aws-cdk/aws-synthetics-alpha": "2.70.0-alpha.0",
20
+ "@aws-cdk/aws-synthetics-alpha": "^2.78.0-alpha.0",
21
21
  "@types/geojson": "^7946.0.10",
22
- "aws-cdk-lib": "2.70.0",
23
- "aws-sdk": "^2.1343.0",
24
- "axios": "^1.3.4",
22
+ "aws-cdk-lib": "^2.78.0",
23
+ "aws-sdk": "^2.1374.0",
24
+ "axios": "^1.2.6",
25
25
  "change-case": "^4.1.2",
26
- "constructs": "^10.1.292",
26
+ "constructs": "^10.2.17",
27
27
  "geojson-validation": "^1.0.2",
28
28
  "moment": "^2.29.4",
29
29
  "node-ttl": "^0.2.0",
30
30
  "pg-native": "^3.0.1",
31
- "pg-promise": "^10.12.0"
31
+ "pg-promise": "^11.0.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@aws-cdk/aws-synthetics-alpha": "2.70.0-alpha.0",
35
- "@types/aws-lambda": "^8.10.114",
34
+ "@aws-cdk/aws-synthetics-alpha": "2.78.0-alpha.0",
35
+ "@types/aws-lambda": "~8.10.115",
36
36
  "@types/geojson": "^7946.0.10",
37
- "@types/jest": "^29.5.0",
38
- "@types/lodash": "^4.14.192",
39
- "@types/node": "^18.15.10",
40
- "@types/ramda": "^0.28.23",
41
- "@types/sinon": "^10.0.13",
42
- "@typescript-eslint/eslint-plugin": "~5.56.0",
43
- "@typescript-eslint/parser": "^5.56.0",
44
- "aws-cdk-lib": "^2.70.0",
45
- "aws-sdk": "^2.1343.0",
46
- "axios": "^1.3.4",
37
+ "@types/jest": "^29.5.1",
38
+ "@types/lodash": "^4.14.194",
39
+ "@types/node": "18.15.13",
40
+ "@types/ramda": "~0.29.1",
41
+ "@types/sinon": "10.0.14",
42
+ "@typescript-eslint/eslint-plugin": "~5.59.5",
43
+ "@typescript-eslint/parser": "^5.59.5",
44
+ "aws-cdk-lib": "~2.78.0",
45
+ "aws-sdk": "~2.1374.0",
46
+ "axios": "^1.3.6",
47
47
  "change-case": "^4.1.2",
48
- "constructs": "^10.1.292",
49
- "eslint": "~8.36.0",
48
+ "constructs": "10.2.17",
49
+ "eslint": "~8.40.0",
50
50
  "eslint-config-prettier": "^8.8.0",
51
- "eslint-plugin-deprecation": "~1.3.3",
51
+ "eslint-plugin-deprecation": "~1.4.1",
52
52
  "geojson-validation": "^1.0.2",
53
53
  "husky": "^8.0.3",
54
54
  "jest": "^29.5.0",
55
- "jest-junit": "^15.0.0",
56
- "lint-staged": "^13.2.0",
55
+ "jest-junit": "^16.0.0",
56
+ "lint-staged": "^13.2.2",
57
57
  "moment": "^2.29.4",
58
58
  "node-ttl": "^0.2.0",
59
59
  "pg-native": "^3.0.1",
60
- "pg-promise": "^10.12.0",
61
- "prettier": "^2.8.7",
62
- "ramda": "^0.28.0",
63
- "rimraf": "^4.1.0",
64
- "sinon": "^15.0.3",
65
- "ts-jest": "^29.0.5",
66
- "typescript": "~4.9.5"
60
+ "pg-promise": "^11.4.3",
61
+ "prettier": "^2.8.8",
62
+ "ramda": "~0.29.0",
63
+ "rimraf": "^5.0.0",
64
+ "sinon": "15.0.4",
65
+ "ts-jest": "^29.1.0",
66
+ "typescript": "~4.9.5",
67
+ "velocityjs": "2.0.6"
67
68
  },
68
69
  "externals": [
69
70
  "aws-sdk",
@@ -18,19 +18,23 @@ import { dateFromIsoString } from "../../../utils/date-utils";
18
18
  * non-textual content.
19
19
  *
20
20
  * If fileName is set, then Content-Disposition-header will be set to use it
21
+ * If timestamp is set, then ETag & Last-Modified headers will be set
21
22
  */
22
23
  export const RESPONSE_DEFAULT_LAMBDA = `#set($inputRoot = $input.path('$'))
23
- $util.base64Decode($inputRoot.body)
24
24
  #if ($inputRoot.status != 200)
25
25
  #set ($context.responseOverride.status = $inputRoot.status)
26
26
  #set ($context.responseOverride.header.Content-Type = 'text/plain')
27
27
  #end
28
28
  #set ($context.responseOverride.header.Access-Control-Allow-Origin = '*')
29
+ #if ("$!inputRoot.timestamp" != "")
30
+ #set ($context.responseOverride.header.ETag = $inputRoot.timestamp)
31
+ #set ($context.responseOverride.header.Last-Modified = $inputRoot.timestamp)
32
+ #end
29
33
  #if ("$!inputRoot.fileName" != "")
30
34
  #set ($disposition = 'attachment; filename="FN"')
31
35
  #set ($context.responseOverride.header.Content-Disposition = $disposition.replaceAll('FN', $inputRoot.fileName))
32
36
  #end
33
- `;
37
+ $util.base64Decode($inputRoot.body)`;
34
38
 
35
39
  /**
36
40
  * Use this for deprecated integrations.
@@ -1,4 +1,4 @@
1
- import { Duration, RemovalPolicy, SecretValue, Stack } from "aws-cdk-lib";
1
+ import { Duration, RemovalPolicy, Stack } from "aws-cdk-lib";
2
2
  import {
3
3
  InstanceType,
4
4
  IVpc,
@@ -19,8 +19,9 @@ import {
19
19
  ParameterGroup,
20
20
  } from "aws-cdk-lib/aws-rds";
21
21
  import { Construct } from "constructs";
22
- import { exportValue, importVpc } from "../import-util";
22
+ import { Secret } from "aws-cdk-lib/aws-secretsmanager";
23
23
  import { InfraStackConfiguration } from "./intra-stack-configuration";
24
+ import { exportValue, importVpc } from "../import-util";
24
25
 
25
26
  export interface DbConfiguration {
26
27
  readonly secretArn: string;
@@ -32,9 +33,6 @@ export interface DbConfiguration {
32
33
  readonly customParameterGroup: boolean;
33
34
  readonly securityGroupId: string;
34
35
 
35
- readonly superuserName: string;
36
- readonly superuserPassword: string;
37
-
38
36
  readonly proxy: {
39
37
  readonly name?: string;
40
38
  readonly securityGroupId: string;
@@ -126,6 +124,12 @@ export class DbStack extends Stack {
126
124
  securityGroup: ISecurityGroup,
127
125
  parameterGroup: IParameterGroup
128
126
  ): DatabaseClusterProps {
127
+ const secret = Secret.fromSecretCompleteArn(
128
+ this,
129
+ "DBSecret",
130
+ configuration.secretArn
131
+ );
132
+
129
133
  return {
130
134
  engine: DatabaseClusterEngine.auroraPostgres({
131
135
  version: configuration.dbVersion,
@@ -155,8 +159,8 @@ export class DbStack extends Stack {
155
159
  parameterGroup,
156
160
  },
157
161
  credentials: Credentials.fromPassword(
158
- configuration.superuserName,
159
- SecretValue.unsafePlainText(configuration.superuserPassword)
162
+ secret.secretValueFromJson("db.superuser").unsafeUnwrap(),
163
+ secret.secretValueFromJson("db.superuser.password")
160
164
  ),
161
165
  parameterGroup,
162
166
  };
@@ -200,10 +204,7 @@ export class DbStack extends Stack {
200
204
  }
201
205
  cfnInstances.forEach((cfnInstance) => delete cfnInstance.engineVersion);
202
206
 
203
- cluster.node.addDependency(
204
- parameterGroup,
205
- "Create ParameterGroup before DatabaseCluster"
206
- );
207
+ cluster.node.addDependency(parameterGroup);
207
208
 
208
209
  return cluster;
209
210
  }
@@ -53,7 +53,9 @@ export class NetworkStack extends Stack {
53
53
  createVpc(configuration: NetworkConfiguration): Vpc {
54
54
  return new Vpc(this, "DigitrafficVPC", {
55
55
  vpcName: configuration.vpcName,
56
- availabilityZones: ["eu-west-1a", "eu-west-1b"],
56
+ availabilityZones: Stack.of(this)
57
+ .availabilityZones.sort()
58
+ .slice(0, 2), // take two first azs
57
59
  enableDnsHostnames: true,
58
60
  enableDnsSupport: true,
59
61
  ipAddresses: IpAddresses.cidr(configuration.cidr),
@@ -2,11 +2,27 @@ export class LambdaResponse {
2
2
  readonly status: number;
3
3
  readonly body: string;
4
4
  readonly fileName?: string;
5
+ readonly timestamp?: string;
5
6
 
6
- constructor(status: number, body: string, fileName?: string) {
7
+ constructor(
8
+ status: number,
9
+ body: string,
10
+ fileName?: string,
11
+ timestamp?: Date
12
+ ) {
7
13
  this.status = status;
8
14
  this.body = body;
9
15
  this.fileName = fileName;
16
+ this.timestamp = timestamp?.toUTCString();
17
+ }
18
+
19
+ withTimestamp(timestamp: Date) {
20
+ return new LambdaResponse(
21
+ this.status,
22
+ this.body,
23
+ this.fileName,
24
+ timestamp
25
+ );
10
26
  }
11
27
 
12
28
  /**
@@ -27,54 +43,54 @@ export class LambdaResponse {
27
43
  * Create LambdaResponse for HTTP 200 from base64-encoded data.
28
44
  */
29
45
  static okBinary(base64: string, fileName?: string) {
30
- return createForBase64(200, base64, fileName);
46
+ return this.createForBase64(200, base64, fileName);
31
47
  }
32
48
 
33
49
  /**
34
50
  * Create LambdaResponse for HTTP 400
35
51
  */
36
52
  static badRequest(body: string) {
37
- return createForString(400, body);
53
+ return this.createForString(400, body);
38
54
  }
39
55
 
40
56
  /**
41
57
  * Create LambdaResponse for HTTP 404
42
58
  */
43
59
  static notFound() {
44
- return createForString(404, "Not found");
60
+ return this.createForString(404, "Not found");
45
61
  }
46
62
 
47
63
  /**
48
64
  * Create LambdaResponse for HTTP 500
49
65
  */
50
66
  static internalError() {
51
- return createForString(500, "Internal error");
67
+ return this.createForString(500, "Internal error");
52
68
  }
53
69
 
54
70
  /**
55
71
  * Create LambdaResponse for HTTP 501
56
72
  */
57
73
  static notImplemented() {
58
- return createForString(501, "Not implemented");
74
+ return this.createForString(501, "Not implemented");
59
75
  }
60
- }
61
76
 
62
- function toBase64(body: string) {
63
- return Buffer.from(body).toString("base64");
64
- }
77
+ private static createForString(
78
+ status: number,
79
+ body: string,
80
+ fileName?: string
81
+ ): LambdaResponse {
82
+ return this.createForBase64(status, toBase64(body), fileName);
83
+ }
65
84
 
66
- function createForString(
67
- status: number,
68
- body: string,
69
- fileName?: string
70
- ): LambdaResponse {
71
- return createForBase64(status, toBase64(body), fileName);
85
+ private static createForBase64(
86
+ status: number,
87
+ base64Body: string,
88
+ fileName?: string
89
+ ): LambdaResponse {
90
+ return new LambdaResponse(status, base64Body, fileName);
91
+ }
72
92
  }
73
93
 
74
- function createForBase64(
75
- status: number,
76
- base64Body: string,
77
- fileName?: string
78
- ): LambdaResponse {
79
- return new LambdaResponse(status, base64Body, fileName);
94
+ function toBase64(body: string) {
95
+ return Buffer.from(body).toString("base64");
80
96
  }
@@ -1,14 +1,16 @@
1
+ import { ValueOf } from "./util-types";
2
+
1
3
  /**
2
4
  * Adds `null` as an accepted type to all properties in given type.
3
5
  */
4
6
  export type Nullable<Obj> = { [Key in keyof Obj]: Obj[Key] | null };
5
7
 
6
- type RequiredKeys<Obj> = {
8
+ type RequiredKeys<Obj> = ValueOf<{
7
9
  [Key in keyof Obj]-?: object extends { [K in Key]: Obj[Key] } ? never : Key;
8
- }[keyof Obj];
9
- type OptionalKeys<Obj> = {
10
+ }>;
11
+ type OptionalKeys<Obj> = ValueOf<{
10
12
  [Key in keyof Obj]-?: object extends { [K in Key]: Obj[Key] } ? Key : never;
11
- }[keyof Obj];
13
+ }>;
12
14
  type RequiredProperties<Obj> = Pick<Obj, RequiredKeys<Obj>>;
13
15
  type OptionalProperties<Obj> = Pick<Obj, OptionalKeys<Obj>>;
14
16
 
@@ -0,0 +1 @@
1
+ export type ValueOf<Obj> = Obj[keyof Obj];