@lighthouse/common 5.0.0-canary-1 → 5.0.0-canary-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.
- package/.vscode/tasks.json +1 -1
- package/dist/helpers/array-buffer-to-base-64/index.js +13 -0
- package/dist/helpers/fetch-image/index.js +9 -45
- package/dist/helpers/fetch-image-for-pdf-generator-service/index.js +2 -2
- package/dist/helpers/fetch-image-for-web/index.js +80 -0
- package/dist/helpers/fetch-lighthouse-logo/index.js +57 -0
- package/dist/helpers/image-validators/index.js +40 -0
- package/lib/helpers/array-buffer-to-base-64/index.js +9 -0
- package/lib/helpers/array-buffer-to-base-64/index.js.map +1 -0
- package/lib/helpers/fetch-image/index.js +12 -52
- package/lib/helpers/fetch-image/index.js.map +1 -1
- package/lib/helpers/fetch-image-for-pdf-generator-service/index.js +2 -2
- package/lib/helpers/fetch-image-for-pdf-generator-service/index.js.map +1 -1
- package/lib/helpers/fetch-image-for-web/index.js +111 -0
- package/lib/helpers/fetch-image-for-web/index.js.map +1 -0
- package/lib/helpers/fetch-lighthouse-logo/index.js +84 -0
- package/lib/helpers/fetch-lighthouse-logo/index.js.map +1 -0
- package/lib/helpers/image-validators/index.js +37 -0
- package/lib/helpers/image-validators/index.js.map +1 -0
- package/mise.toml +1 -1
- package/package.json +1 -1
package/.vscode/tasks.json
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.arrayBufferToBase64 = arrayBufferToBase64;
|
|
7
|
+
|
|
8
|
+
function arrayBufferToBase64(buffer) {
|
|
9
|
+
let binary = '';
|
|
10
|
+
const bytes = [].slice.call(new Uint8Array(buffer));
|
|
11
|
+
bytes.forEach(b => binary += String.fromCharCode(b));
|
|
12
|
+
return btoa(binary);
|
|
13
|
+
}
|
|
@@ -22,6 +22,8 @@ var _images = require("../../images");
|
|
|
22
22
|
|
|
23
23
|
var _fetchImageForPdfGeneratorService = require("../fetch-image-for-pdf-generator-service");
|
|
24
24
|
|
|
25
|
+
var _fetchImageForWeb = require("../fetch-image-for-web");
|
|
26
|
+
|
|
25
27
|
// NOTE use the native fetch if it's available in the browser, because the
|
|
26
28
|
// ponyfill (which actually uses the github polyfill) does not support all the
|
|
27
29
|
// same options as native fetch
|
|
@@ -51,56 +53,18 @@ function fetchImage(url, options = {}) {
|
|
|
51
53
|
const shouldUseCloudfront = url && url.includes('.cloudfront.net');
|
|
52
54
|
|
|
53
55
|
if (shouldUseCloudfront && isWebContext) {
|
|
56
|
+
// NOTE: Instead of passing in options directly
|
|
57
|
+
// we expose the used variables for readability
|
|
54
58
|
const {
|
|
55
59
|
Signature,
|
|
56
60
|
Policy,
|
|
57
61
|
KeyPairId
|
|
58
62
|
} = options;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const contentHeader = response.headers.get('content-length');
|
|
65
|
-
const contentType = response.headers.get('content-type'); // NOTE: the response will be ok but we won't be able to render any
|
|
66
|
-
// image meaning pdfmake will error. Raise error here and return early.
|
|
67
|
-
|
|
68
|
-
if (contentHeader === '0') {
|
|
69
|
-
return _bluebird.default.reject(new Error(`Failed to fetch image as no content length: ${encodedUrl}`));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!response.ok) {
|
|
73
|
-
return _bluebird.default.reject(new Error(`Failed to fetch image: ${encodedUrl}`));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const imageType = contentTypes[contentType];
|
|
77
|
-
return response.arrayBuffer().then(buffer => ({
|
|
78
|
-
buffer,
|
|
79
|
-
imageType
|
|
80
|
-
}));
|
|
81
|
-
}).then(({
|
|
82
|
-
buffer,
|
|
83
|
-
imageType
|
|
84
|
-
}) => {
|
|
85
|
-
const base64Flag = `data:image/${imageType};base64,`;
|
|
86
|
-
const imageStr = arrayBufferToBase64(buffer);
|
|
87
|
-
const base64 = `${base64Flag}${imageStr}`;
|
|
88
|
-
const isValid = validateBase64Image(base64);
|
|
89
|
-
|
|
90
|
-
if (!isValid) {
|
|
91
|
-
return _bluebird.default.reject(new Error('InvalidImageError'));
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return base64;
|
|
95
|
-
}).catch(error => {
|
|
96
|
-
if (isHeader) {
|
|
97
|
-
// NOTE: Replace failed headers with LH logo
|
|
98
|
-
console.error('FetchImageHeaderError', error);
|
|
99
|
-
return fetchImage(_constants.LIGHTHOUSE_LOGO_URL, defaultOptions);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
console.error(error);
|
|
103
|
-
return _images.imageNotFound;
|
|
63
|
+
return (0, _fetchImageForWeb.fetchImageForWeb)(url, {
|
|
64
|
+
isHeader,
|
|
65
|
+
Signature,
|
|
66
|
+
Policy,
|
|
67
|
+
KeyPairId
|
|
104
68
|
});
|
|
105
69
|
} else if (shouldUseCloudfront && !isWebContext) {
|
|
106
70
|
return (0, _fetchImageForPdfGeneratorService.fetchImageForPdfGeneratorService)(url);
|
|
@@ -122,7 +122,7 @@ async function fetchResourceFromS3({
|
|
|
122
122
|
bucketName,
|
|
123
123
|
key
|
|
124
124
|
}) {
|
|
125
|
-
console.
|
|
125
|
+
console.info(`Fetching resource from S3 Bucket: '${bucketName}' at path: '${key}'`);
|
|
126
126
|
|
|
127
127
|
if (!bucketName || !key) {
|
|
128
128
|
throw new Error('bucketName and key are required for S3 resource fetch ' + JSON.stringify({
|
|
@@ -150,6 +150,6 @@ async function fetchResourceFromS3({
|
|
|
150
150
|
throw new Error(`Failed to fetch image: ${bucketName}/${key}`);
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
console.
|
|
153
|
+
console.info('Image not found in transformed bucket, invoking transformer');
|
|
154
154
|
}
|
|
155
155
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.fetchImageForWeb = void 0;
|
|
9
|
+
|
|
10
|
+
var _images = require("../../images");
|
|
11
|
+
|
|
12
|
+
var _fetchLighthouseLogo = require("../fetch-lighthouse-logo");
|
|
13
|
+
|
|
14
|
+
var _arrayBufferToBase = require("../array-buffer-to-base-64");
|
|
15
|
+
|
|
16
|
+
var _imageValidators = require("../image-validators");
|
|
17
|
+
|
|
18
|
+
var _fetchPonyfill = _interopRequireDefault(require("fetch-ponyfill"));
|
|
19
|
+
|
|
20
|
+
const contentTypes = {
|
|
21
|
+
'image/png': 'png',
|
|
22
|
+
'image/jpeg': 'jpeg'
|
|
23
|
+
}; // NOTE use the native fetch if it's available in the browser, because the
|
|
24
|
+
// ponyfill (which actually uses the github polyfill) does not support all the
|
|
25
|
+
// same options as native fetch
|
|
26
|
+
|
|
27
|
+
const fetch = typeof self === 'object' && self.fetch || (0, _fetchPonyfill.default)({
|
|
28
|
+
Promise
|
|
29
|
+
}).fetch;
|
|
30
|
+
|
|
31
|
+
const fetchImageForWeb = async function (url, options) {
|
|
32
|
+
const {
|
|
33
|
+
isHeader = false,
|
|
34
|
+
Signature,
|
|
35
|
+
Policy,
|
|
36
|
+
KeyPairId
|
|
37
|
+
} = options;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const firstParamConnector = new URL(url).searchParams.size > 0 ? '&' : '?';
|
|
41
|
+
const urlToEncode = `${url}${firstParamConnector}Signature=${Signature}&Policy=${Policy}&Key-Pair-Id=${KeyPairId}`;
|
|
42
|
+
const encodedUrl = encodeURI(urlToEncode);
|
|
43
|
+
console.info('Fetching image via CloudFront For Web');
|
|
44
|
+
const imageResponse = await fetch(encodedUrl);
|
|
45
|
+
const contentLengthHeader = imageResponse.headers.get('content-length');
|
|
46
|
+
const contentType = imageResponse.headers.get('content-type');
|
|
47
|
+
|
|
48
|
+
if (contentLengthHeader === '0') {
|
|
49
|
+
return Promise.reject(new Error(`Failed to fetch image as no content length: ${encodedUrl}`));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!imageResponse.ok) {
|
|
53
|
+
return Promise.reject(new Error(`Failed to fetch image: ${encodedUrl}`));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const imageType = contentTypes[contentType];
|
|
57
|
+
const logoArrayBuffer = await imageResponse.arrayBuffer();
|
|
58
|
+
const base64Flag = `data:image/${imageType};base64,`;
|
|
59
|
+
const imageStr = (0, _arrayBufferToBase.arrayBufferToBase64)(logoArrayBuffer);
|
|
60
|
+
const base64 = `${base64Flag}${imageStr}`;
|
|
61
|
+
const isValid = (0, _imageValidators.validateBase64Image)(base64);
|
|
62
|
+
|
|
63
|
+
if (isValid) {
|
|
64
|
+
return base64;
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
if (isHeader) {
|
|
68
|
+
// NOTE: Replace failed headers with LH logo
|
|
69
|
+
console.error('FetchImageHeaderError', error);
|
|
70
|
+
return (0, _fetchLighthouseLogo.fetchLighthouseLogo)();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.error(error);
|
|
74
|
+
return _images.imageNotFound;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return Promise.reject(new Error('InvalidImageError'));
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
exports.fetchImageForWeb = fetchImageForWeb;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.fetchLighthouseLogo = void 0;
|
|
9
|
+
|
|
10
|
+
var _constants = require("../../constants");
|
|
11
|
+
|
|
12
|
+
var _arrayBufferToBase = require("../array-buffer-to-base-64");
|
|
13
|
+
|
|
14
|
+
var _imageValidators = require("../image-validators");
|
|
15
|
+
|
|
16
|
+
var _fetchPonyfill = _interopRequireDefault(require("fetch-ponyfill"));
|
|
17
|
+
|
|
18
|
+
// NOTE use the native fetch if it's available in the browser, because the
|
|
19
|
+
// ponyfill (which actually uses the github polyfill) does not support all the
|
|
20
|
+
// same options as native fetch
|
|
21
|
+
const fetch = typeof self === 'object' && self.fetch || (0, _fetchPonyfill.default)({
|
|
22
|
+
Promise
|
|
23
|
+
}).fetch;
|
|
24
|
+
const contentTypes = {
|
|
25
|
+
'image/png': 'png',
|
|
26
|
+
'image/jpeg': 'jpeg'
|
|
27
|
+
}; // NOTE: This is not stored where other images are - so it cannot go through CloudFront
|
|
28
|
+
|
|
29
|
+
const fetchLighthouseLogo = async function () {
|
|
30
|
+
const encodedLogoUrl = encodeURI(_constants.LIGHTHOUSE_LOGO_URL);
|
|
31
|
+
const logoResponse = await fetch(encodedLogoUrl);
|
|
32
|
+
const contentLengthHeader = logoResponse.headers.get('content-length');
|
|
33
|
+
const contentType = logoResponse.headers.get('content-type');
|
|
34
|
+
|
|
35
|
+
if (contentLengthHeader === '0') {
|
|
36
|
+
return Promise.reject(new Error(`Failed to fetch image as no content length: ${encodedLogoUrl}`));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!logoResponse.ok) {
|
|
40
|
+
return Promise.reject(new Error(`Failed to fetch image: ${encodedLogoUrl}`));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const imageType = contentTypes[contentType];
|
|
44
|
+
const logoArrayBuffer = await logoResponse.arrayBuffer();
|
|
45
|
+
const base64Flag = `data:image/${imageType};base64,`;
|
|
46
|
+
const imageStr = (0, _arrayBufferToBase.arrayBufferToBase64)(logoArrayBuffer);
|
|
47
|
+
const base64 = `${base64Flag}${imageStr}`;
|
|
48
|
+
const isValid = (0, _imageValidators.validateBase64Image)(base64);
|
|
49
|
+
|
|
50
|
+
if (isValid) {
|
|
51
|
+
return base64;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return Promise.reject(new Error('InvalidImageError'));
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
exports.fetchLighthouseLogo = fetchLighthouseLogo;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.validateBase64Image = validateBase64Image;
|
|
7
|
+
|
|
8
|
+
function validateBase64Image(base64String) {
|
|
9
|
+
const isJpeg = base64String.startsWith('data:image/jpeg;base64,');
|
|
10
|
+
if (isJpeg) return validateJpegImage(base64String);
|
|
11
|
+
const isPng = base64String.startsWith('data:image/png;base64,');
|
|
12
|
+
if (isPng) return validatePngImage(base64String);
|
|
13
|
+
return false;
|
|
14
|
+
} // See SO for more info: https://stackoverflow.com/a/41635312
|
|
15
|
+
// Fiddle: https://jsfiddle.net/Lnyxuchw/
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
function validateJpegImage(base64string) {
|
|
19
|
+
const src = base64string;
|
|
20
|
+
const imageData = Uint8Array.from(atob(src.replace('data:image/jpeg;base64,', '')), c => c.charCodeAt(0));
|
|
21
|
+
const imageCorrupted = imageData[imageData.length - 1] === 217 && imageData[imageData.length - 2] === 255;
|
|
22
|
+
return imageCorrupted;
|
|
23
|
+
} // See SO for more info: https://stackoverflow.com/a/41635312
|
|
24
|
+
// Fiddle: https://jsfiddle.net/Lnyxuchw/
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
function validatePngImage(base64string) {
|
|
28
|
+
const src = base64string;
|
|
29
|
+
const imageData = Uint8Array.from(atob(src.replace('data:image/png;base64,', '')), c => c.charCodeAt(0));
|
|
30
|
+
const sequence = [0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]; // in hex:
|
|
31
|
+
//check last 12 elements of array so they contains needed values
|
|
32
|
+
|
|
33
|
+
for (let i = 12; i > 0; i--) {
|
|
34
|
+
if (imageData[imageData.length - i] !== sequence[12 - i]) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/helpers/array-buffer-to-base-64/index.js"],"names":["arrayBufferToBase64","buffer","binary","bytes","slice","call","Uint8Array","forEach","b","String","fromCharCode","btoa"],"mappings":"AAAA,OAAO,SAASA,mBAAT,CAA6BC,MAA7B,EAAqC;AAC1C,MAAIC,MAAM,GAAG,EAAb;AACA,MAAMC,KAAK,GAAG,GAAGC,KAAH,CAASC,IAAT,CAAc,IAAIC,UAAJ,CAAeL,MAAf,CAAd,CAAd;AAEAE,EAAAA,KAAK,CAACI,OAAN,CAAc,UAAAC,CAAC;AAAA,WAAKN,MAAM,IAAIO,MAAM,CAACC,YAAP,CAAoBF,CAApB,CAAf;AAAA,GAAf;AAEA,SAAOG,IAAI,CAACT,MAAD,CAAX;AACD","sourcesContent":["export function arrayBufferToBase64(buffer) {\n let binary = ''\n const bytes = [].slice.call(new Uint8Array(buffer))\n\n bytes.forEach(b => (binary += String.fromCharCode(b)))\n\n return btoa(binary)\n}\n"],"file":"index.js"}
|
|
@@ -10,7 +10,8 @@ import fetchPonyfill from 'fetch-ponyfill';
|
|
|
10
10
|
import Promise from 'bluebird';
|
|
11
11
|
import { LIGHTHOUSE_LOGO_URL } from '../../constants';
|
|
12
12
|
import { imageNotFound } from '../../images';
|
|
13
|
-
import { fetchImageForPdfGeneratorService } from '../fetch-image-for-pdf-generator-service';
|
|
13
|
+
import { fetchImageForPdfGeneratorService } from '../fetch-image-for-pdf-generator-service';
|
|
14
|
+
import { fetchImageForWeb } from '../fetch-image-for-web'; // NOTE use the native fetch if it's available in the browser, because the
|
|
14
15
|
// ponyfill (which actually uses the github polyfill) does not support all the
|
|
15
16
|
// same options as native fetch
|
|
16
17
|
|
|
@@ -39,57 +40,16 @@ export function fetchImage(url) {
|
|
|
39
40
|
var shouldUseCloudfront = url && url.includes('.cloudfront.net');
|
|
40
41
|
|
|
41
42
|
if (shouldUseCloudfront && isWebContext) {
|
|
43
|
+
// NOTE: Instead of passing in options directly
|
|
44
|
+
// we expose the used variables for readability
|
|
42
45
|
var Signature = options.Signature,
|
|
43
46
|
Policy = options.Policy,
|
|
44
47
|
KeyPairId = options.KeyPairId;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
console.info('Fetching image via CloudFront For Web');
|
|
51
|
-
return fetch(_encodedUrl).then(function (response) {
|
|
52
|
-
var contentHeader = response.headers.get('content-length');
|
|
53
|
-
var contentType = response.headers.get('content-type'); // NOTE: the response will be ok but we won't be able to render any
|
|
54
|
-
// image meaning pdfmake will error. Raise error here and return early.
|
|
55
|
-
|
|
56
|
-
if (contentHeader === '0') {
|
|
57
|
-
return Promise.reject(new Error("Failed to fetch image as no content length: ".concat(_encodedUrl)));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (!response.ok) {
|
|
61
|
-
return Promise.reject(new Error("Failed to fetch image: ".concat(_encodedUrl)));
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
var imageType = contentTypes[contentType];
|
|
65
|
-
return response.arrayBuffer().then(function (buffer) {
|
|
66
|
-
return {
|
|
67
|
-
buffer: buffer,
|
|
68
|
-
imageType: imageType
|
|
69
|
-
};
|
|
70
|
-
});
|
|
71
|
-
}).then(function (_ref) {
|
|
72
|
-
var buffer = _ref.buffer,
|
|
73
|
-
imageType = _ref.imageType;
|
|
74
|
-
var base64Flag = "data:image/".concat(imageType, ";base64,");
|
|
75
|
-
var imageStr = arrayBufferToBase64(buffer);
|
|
76
|
-
var base64 = "".concat(base64Flag).concat(imageStr);
|
|
77
|
-
var isValid = validateBase64Image(base64);
|
|
78
|
-
|
|
79
|
-
if (!isValid) {
|
|
80
|
-
return Promise.reject(new Error('InvalidImageError'));
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return base64;
|
|
84
|
-
}).catch(function (error) {
|
|
85
|
-
if (isHeader) {
|
|
86
|
-
// NOTE: Replace failed headers with LH logo
|
|
87
|
-
console.error('FetchImageHeaderError', error);
|
|
88
|
-
return fetchImage(LIGHTHOUSE_LOGO_URL, defaultOptions);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
console.error(error);
|
|
92
|
-
return imageNotFound;
|
|
48
|
+
return fetchImageForWeb(url, {
|
|
49
|
+
isHeader: isHeader,
|
|
50
|
+
Signature: Signature,
|
|
51
|
+
Policy: Policy,
|
|
52
|
+
KeyPairId: KeyPairId
|
|
93
53
|
});
|
|
94
54
|
} else if (shouldUseCloudfront && !isWebContext) {
|
|
95
55
|
return fetchImageForPdfGeneratorService(url);
|
|
@@ -121,9 +81,9 @@ export function fetchImage(url) {
|
|
|
121
81
|
imageType: imageType
|
|
122
82
|
};
|
|
123
83
|
});
|
|
124
|
-
}).then(function (
|
|
125
|
-
var buffer =
|
|
126
|
-
imageType =
|
|
84
|
+
}).then(function (_ref) {
|
|
85
|
+
var buffer = _ref.buffer,
|
|
86
|
+
imageType = _ref.imageType;
|
|
127
87
|
var base64Flag = "data:image/".concat(imageType, ";base64,");
|
|
128
88
|
var imageStr = arrayBufferToBase64(buffer);
|
|
129
89
|
var base64 = "".concat(base64Flag).concat(imageStr);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/helpers/fetch-image/index.js"],"names":["atob","btoa","fetchPonyfill","Promise","LIGHTHOUSE_LOGO_URL","imageNotFound","fetchImageForPdfGeneratorService","fetch","self","contentTypes","defaultOptions","cache","fetchImage","url","options","isWebContext","window","isHeader","shouldUseCloudfront","includes","Signature","Policy","KeyPairId","firstParamConnector","URL","searchParams","size","urlToEncode","encodedUrl","encodeURI","console","info","then","response","contentHeader","headers","get","contentType","reject","Error","ok","imageType","arrayBuffer","buffer","base64Flag","imageStr","arrayBufferToBase64","base64","isValid","validateBase64Image","catch","error","fetchOptions","binary","bytes","slice","call","Uint8Array","forEach","b","String","fromCharCode","base64String","isJpeg","startsWith","validateJpegImage","isPng","validatePngImage","base64string","src","imageData","from","replace","c","charCodeAt","imageCorrupted","length","sequence","i"],"mappings":"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","sourcesContent":["import { atob, btoa } from '@lighthouse/abab'\nimport fetchPonyfill from 'fetch-ponyfill'\nimport Promise from 'bluebird'\nimport { LIGHTHOUSE_LOGO_URL } from '../../constants'\nimport { imageNotFound } from '../../images'\nimport { fetchImageForPdfGeneratorService } from '../fetch-image-for-pdf-generator-service'\n\n// NOTE use the native fetch if it's available in the browser, because the\n// ponyfill (which actually uses the github polyfill) does not support all the\n// same options as native fetch\nconst fetch =\n (typeof self === 'object' && self.fetch) || fetchPonyfill({ Promise }).fetch\n\nconst contentTypes = {\n 'image/png': 'png',\n 'image/jpeg': 'jpeg',\n}\n\nconst defaultOptions = {\n // NOTE The cache: no-cache option is important to avoid an issue with CORS\n // and caching on Chrome. Here's a good explanation of the issue:\n // https://stackoverflow.com/a/37455118\n // In our case, when loading the web version of a form, the signature image is\n // cached without the correct CORS headers. If the pdf is then generated,\n // there's a mismatch between the cached image headers and the CORS headers\n // sent from the fetch request, causing an error\n cache: 'no-cache',\n}\n\nexport function fetchImage(url, options = {}) {\n const isWebContext = typeof window === 'object'\n\n const { isHeader = false } = options\n\n const shouldUseCloudfront = url && url.includes('.cloudfront.net')\n\n if (shouldUseCloudfront && isWebContext) {\n const { Signature, Policy, KeyPairId } = options\n\n const firstParamConnector = new URL(url).searchParams.size > 0 ? '&' : '?'\n const urlToEncode = `${url}${firstParamConnector}Signature=${Signature}&Policy=${Policy}&Key-Pair-Id=${KeyPairId}`\n\n const encodedUrl = encodeURI(urlToEncode)\n\n console.info('Fetching image via CloudFront For Web')\n\n return fetch(encodedUrl)\n .then(response => {\n const contentHeader = response.headers.get('content-length')\n const contentType = response.headers.get('content-type')\n\n // NOTE: the response will be ok but we won't be able to render any\n // image meaning pdfmake will error. Raise error here and return early.\n if (contentHeader === '0') {\n return Promise.reject(\n new Error(\n `Failed to fetch image as no content length: ${encodedUrl}`\n )\n )\n }\n\n if (!response.ok) {\n return Promise.reject(\n new Error(`Failed to fetch image: ${encodedUrl}`)\n )\n }\n\n const imageType = contentTypes[contentType]\n\n return response.arrayBuffer().then(buffer => ({\n buffer,\n imageType,\n }))\n })\n .then(({ buffer, imageType }) => {\n const base64Flag = `data:image/${imageType};base64,`\n const imageStr = arrayBufferToBase64(buffer)\n\n const base64 = `${base64Flag}${imageStr}`\n const isValid = validateBase64Image(base64)\n\n if (!isValid) {\n return Promise.reject(new Error('InvalidImageError'))\n }\n\n return base64\n })\n .catch(error => {\n if (isHeader) {\n // NOTE: Replace failed headers with LH logo\n console.error('FetchImageHeaderError', error)\n return fetchImage(LIGHTHOUSE_LOGO_URL, defaultOptions)\n }\n\n console.error(error)\n return imageNotFound\n })\n } else if (shouldUseCloudfront && !isWebContext) {\n return fetchImageForPdfGeneratorService(url)\n }\n\n const encodedUrl = encodeURI(url)\n\n const fetchOptions = {\n ...defaultOptions,\n ...options,\n headers: {\n ...(options.headers || {}),\n },\n }\n\n return fetch(encodedUrl, fetchOptions)\n .then(response => {\n const contentHeader = response.headers.get('content-length')\n const contentType = response.headers.get('content-type')\n\n // NOTE: the response will be ok but we won't be able to render any\n // image meaning pdfmake will error. Raise error here and return early.\n if (contentHeader === '0') {\n return Promise.reject(\n new Error(`Failed to fetch image as no content length: ${encodedUrl}`)\n )\n }\n\n if (!response.ok) {\n return Promise.reject(new Error(`Failed to fetch image: ${encodedUrl}`))\n }\n\n const imageType = contentTypes[contentType]\n\n return response.arrayBuffer().then(buffer => ({\n buffer,\n imageType,\n }))\n })\n .then(({ buffer, imageType }) => {\n const base64Flag = `data:image/${imageType};base64,`\n const imageStr = arrayBufferToBase64(buffer)\n\n const base64 = `${base64Flag}${imageStr}`\n const isValid = validateBase64Image(base64)\n\n if (!isValid) {\n return Promise.reject(new Error('InvalidImageError'))\n }\n\n return base64\n })\n .catch(error => {\n if (isHeader) {\n // NOTE: Replace failed headers with LH logo\n console.error('FetchImageHeaderError', error)\n return fetchImage(LIGHTHOUSE_LOGO_URL, defaultOptions)\n }\n\n console.error(error)\n return imageNotFound\n })\n}\n\nfunction arrayBufferToBase64(buffer) {\n let binary = ''\n const bytes = [].slice.call(new Uint8Array(buffer))\n\n bytes.forEach(b => (binary += String.fromCharCode(b)))\n\n return btoa(binary)\n}\n\nexport function validateBase64Image(base64String) {\n const isJpeg = base64String.startsWith('data:image/jpeg;base64,')\n\n if (isJpeg) return validateJpegImage(base64String)\n\n const isPng = base64String.startsWith('data:image/png;base64,')\n\n if (isPng) return validatePngImage(base64String)\n\n return false\n}\n\n// See SO for more info: https://stackoverflow.com/a/41635312\n// Fiddle: https://jsfiddle.net/Lnyxuchw/\nexport function validateJpegImage(base64string) {\n const src = base64string\n const imageData = Uint8Array.from(\n atob(src.replace('data:image/jpeg;base64,', '')),\n c => c.charCodeAt(0)\n )\n const imageCorrupted =\n imageData[imageData.length - 1] === 217 &&\n imageData[imageData.length - 2] === 255\n\n return imageCorrupted\n}\n\n// See SO for more info: https://stackoverflow.com/a/41635312\n// Fiddle: https://jsfiddle.net/Lnyxuchw/\nexport function validatePngImage(base64string) {\n const src = base64string\n const imageData = Uint8Array.from(\n atob(src.replace('data:image/png;base64,', '')),\n c => c.charCodeAt(0)\n )\n const sequence = [0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130] // in hex:\n\n //check last 12 elements of array so they contains needed values\n for (let i = 12; i > 0; i--) {\n if (imageData[imageData.length - i] !== sequence[12 - i]) {\n return false\n }\n }\n\n return true\n}\n"],"file":"index.js"}
|
|
1
|
+
{"version":3,"sources":["../../../src/helpers/fetch-image/index.js"],"names":["atob","btoa","fetchPonyfill","Promise","LIGHTHOUSE_LOGO_URL","imageNotFound","fetchImageForPdfGeneratorService","fetchImageForWeb","fetch","self","contentTypes","defaultOptions","cache","fetchImage","url","options","isWebContext","window","isHeader","shouldUseCloudfront","includes","Signature","Policy","KeyPairId","encodedUrl","encodeURI","fetchOptions","headers","then","response","contentHeader","get","contentType","reject","Error","ok","imageType","arrayBuffer","buffer","base64Flag","imageStr","arrayBufferToBase64","base64","isValid","validateBase64Image","catch","error","console","binary","bytes","slice","call","Uint8Array","forEach","b","String","fromCharCode","base64String","isJpeg","startsWith","validateJpegImage","isPng","validatePngImage","base64string","src","imageData","from","replace","c","charCodeAt","imageCorrupted","length","sequence","i"],"mappings":"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","sourcesContent":["import { atob, btoa } from '@lighthouse/abab'\nimport fetchPonyfill from 'fetch-ponyfill'\nimport Promise from 'bluebird'\nimport { LIGHTHOUSE_LOGO_URL } from '../../constants'\nimport { imageNotFound } from '../../images'\nimport { fetchImageForPdfGeneratorService } from '../fetch-image-for-pdf-generator-service'\nimport { fetchImageForWeb } from '../fetch-image-for-web'\n\n// NOTE use the native fetch if it's available in the browser, because the\n// ponyfill (which actually uses the github polyfill) does not support all the\n// same options as native fetch\nconst fetch =\n (typeof self === 'object' && self.fetch) || fetchPonyfill({ Promise }).fetch\n\nconst contentTypes = {\n 'image/png': 'png',\n 'image/jpeg': 'jpeg',\n}\n\nconst defaultOptions = {\n // NOTE The cache: no-cache option is important to avoid an issue with CORS\n // and caching on Chrome. Here's a good explanation of the issue:\n // https://stackoverflow.com/a/37455118\n // In our case, when loading the web version of a form, the signature image is\n // cached without the correct CORS headers. If the pdf is then generated,\n // there's a mismatch between the cached image headers and the CORS headers\n // sent from the fetch request, causing an error\n cache: 'no-cache',\n}\n\nexport function fetchImage(url, options = {}) {\n const isWebContext = typeof window === 'object'\n\n const { isHeader = false } = options\n\n const shouldUseCloudfront = url && url.includes('.cloudfront.net')\n\n if (shouldUseCloudfront && isWebContext) {\n // NOTE: Instead of passing in options directly\n // we expose the used variables for readability\n const { Signature, Policy, KeyPairId } = options\n\n return fetchImageForWeb(url, {\n isHeader,\n Signature,\n Policy,\n KeyPairId,\n })\n } else if (shouldUseCloudfront && !isWebContext) {\n return fetchImageForPdfGeneratorService(url)\n }\n\n const encodedUrl = encodeURI(url)\n\n const fetchOptions = {\n ...defaultOptions,\n ...options,\n headers: {\n ...(options.headers || {}),\n },\n }\n\n return fetch(encodedUrl, fetchOptions)\n .then(response => {\n const contentHeader = response.headers.get('content-length')\n const contentType = response.headers.get('content-type')\n\n // NOTE: the response will be ok but we won't be able to render any\n // image meaning pdfmake will error. Raise error here and return early.\n if (contentHeader === '0') {\n return Promise.reject(\n new Error(`Failed to fetch image as no content length: ${encodedUrl}`)\n )\n }\n\n if (!response.ok) {\n return Promise.reject(new Error(`Failed to fetch image: ${encodedUrl}`))\n }\n\n const imageType = contentTypes[contentType]\n\n return response.arrayBuffer().then(buffer => ({\n buffer,\n imageType,\n }))\n })\n .then(({ buffer, imageType }) => {\n const base64Flag = `data:image/${imageType};base64,`\n const imageStr = arrayBufferToBase64(buffer)\n\n const base64 = `${base64Flag}${imageStr}`\n const isValid = validateBase64Image(base64)\n\n if (!isValid) {\n return Promise.reject(new Error('InvalidImageError'))\n }\n\n return base64\n })\n .catch(error => {\n if (isHeader) {\n // NOTE: Replace failed headers with LH logo\n console.error('FetchImageHeaderError', error)\n return fetchImage(LIGHTHOUSE_LOGO_URL, defaultOptions)\n }\n\n console.error(error)\n return imageNotFound\n })\n}\n\nfunction arrayBufferToBase64(buffer) {\n let binary = ''\n const bytes = [].slice.call(new Uint8Array(buffer))\n\n bytes.forEach(b => (binary += String.fromCharCode(b)))\n\n return btoa(binary)\n}\n\nexport function validateBase64Image(base64String) {\n const isJpeg = base64String.startsWith('data:image/jpeg;base64,')\n\n if (isJpeg) return validateJpegImage(base64String)\n\n const isPng = base64String.startsWith('data:image/png;base64,')\n\n if (isPng) return validatePngImage(base64String)\n\n return false\n}\n\n// See SO for more info: https://stackoverflow.com/a/41635312\n// Fiddle: https://jsfiddle.net/Lnyxuchw/\nexport function validateJpegImage(base64string) {\n const src = base64string\n const imageData = Uint8Array.from(\n atob(src.replace('data:image/jpeg;base64,', '')),\n c => c.charCodeAt(0)\n )\n const imageCorrupted =\n imageData[imageData.length - 1] === 217 &&\n imageData[imageData.length - 2] === 255\n\n return imageCorrupted\n}\n\n// See SO for more info: https://stackoverflow.com/a/41635312\n// Fiddle: https://jsfiddle.net/Lnyxuchw/\nexport function validatePngImage(base64string) {\n const src = base64string\n const imageData = Uint8Array.from(\n atob(src.replace('data:image/png;base64,', '')),\n c => c.charCodeAt(0)\n )\n const sequence = [0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130] // in hex:\n\n //check last 12 elements of array so they contains needed values\n for (let i = 12; i > 0; i--) {\n if (imageData[imageData.length - i] !== sequence[12 - i]) {\n return false\n }\n }\n\n return true\n}\n"],"file":"index.js"}
|
|
@@ -190,7 +190,7 @@ function _fetchResourceFromS() {
|
|
|
190
190
|
switch (_context3.prev = _context3.next) {
|
|
191
191
|
case 0:
|
|
192
192
|
bucketName = _ref2.bucketName, key = _ref2.key;
|
|
193
|
-
console.
|
|
193
|
+
console.info("Fetching resource from S3 Bucket: '".concat(bucketName, "' at path: '").concat(key, "'"));
|
|
194
194
|
|
|
195
195
|
if (!(!bucketName || !key)) {
|
|
196
196
|
_context3.next = 4;
|
|
@@ -233,7 +233,7 @@ function _fetchResourceFromS() {
|
|
|
233
233
|
throw new Error("Failed to fetch image: ".concat(bucketName, "/").concat(key));
|
|
234
234
|
|
|
235
235
|
case 16:
|
|
236
|
-
console.
|
|
236
|
+
console.info('Image not found in transformed bucket, invoking transformer');
|
|
237
237
|
|
|
238
238
|
case 17:
|
|
239
239
|
case "end":
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/helpers/fetch-image-for-pdf-generator-service/index.js"],"names":["AWS","REGION","process","env","AWS_REGION","lambda","Lambda","region","s3","S3","fetchImageForPdfGeneratorService","url","console","info","Error","urlMatch","match","applicationId","key","split","fetchResourceFromS3","bucketName","S3_BUCKET_UPLOADS","alreadyTransformedImage","body","toString","requestImageTransformation","transformerResponse","statusCode","log","redirectLocation","headers","Location","newlyTransformedImage","lambdaEvent","requestContext","http","method","path","params","FunctionName","IMAGE_TRANSFORMER_ARN","InvocationType","Payload","JSON","stringify","invoke","promise","result","response","parse","errorMessage","message","error","Bucket","Key","getObject","Body","contentType","ContentType","contentLength","ContentLength","lastModified","LastModified","etag","ETag","code"],"mappings":";;AAAA,OAAOA,GAAP,MAAgB,SAAhB;AAEA,IAAMC,MAAM,GAAGC,OAAO,CAACC,GAAR,CAAYC,UAA3B;AAEA,IAAMC,MAAM,GAAG,IAAIL,GAAG,CAACM,MAAR,CAAe;AAC5BC,EAAAA,MAAM,EAAEN;AADoB,CAAf,CAAf;AAGA,IAAMO,EAAE,GAAG,IAAIR,GAAG,CAACS,EAAR,CAAW;AACpBF,EAAAA,MAAM,EAAEN;AADY,CAAX,CAAX;AAIA,OAAO,IAAMS,gCAAgC;AAAA,sEAAG,iBAAeC,GAAf;AAAA;;AAAA;AAAA;AAAA;AAAA;AAC9CC,YAAAA,OAAO,CAACC,IAAR,CACE,oEADF;;AAD8C,gBAKzCF,GALyC;AAAA;AAAA;AAAA;;AAAA,kBAMtC,IAAIG,KAAJ,CAAU,0DAAV,CANsC;;AAAA;AASxCC,YAAAA,QATwC,GAS7BJ,GAAG,IAAIA,GAAG,CAACK,KAAJ,CAAU,kBAAV,CATsB;AAUxCC,YAAAA,aAVwC,GAUxBF,QAAQ,IAAIA,QAAQ,CAAC,CAAD,CAVI;;AAAA,gBAYzCE,aAZyC;AAAA;AAAA;AAAA;;AAAA,kBAatC,IAAIH,KAAJ,CAAU,wCAAV,CAbsC;;AAAA;AAgBxCI,YAAAA,GAhBwC,GAgBlCP,GAAG,CAACQ,KAAJ,CAAUF,aAAV,EAAyB,CAAzB,CAhBkC;AAAA;AAAA,mBAkBRG,mBAAmB,CAAC;AACxDC,cAAAA,UAAU,YAAKnB,OAAO,CAACC,GAAR,CAAYmB,iBAAjB,iBAD8C;AAExDJ,cAAAA,GAAG,EAAHA;AAFwD,aAAD,CAlBX;;AAAA;AAkBxCK,YAAAA,uBAlBwC;;AAAA,kBAuB1CA,uBAAuB,IAAIA,uBAAuB,CAACC,IAvBT;AAAA;AAAA;AAAA;;AAAA,6CAwBrCD,uBAAuB,CAACC,IAAxB,CAA6BC,QAA7B,CAAsC,QAAtC,CAxBqC;;AAAA;AAAA;AAAA,mBA2BZC,0BAA0B,CAACR,GAAD,CA3Bd;;AAAA;AA2BxCS,YAAAA,mBA3BwC;AA6BxCC,YAAAA,UA7BwC,GA6B3BD,mBAAmB,CAACC,UA7BO;AAAA,0BA+BtCA,UA/BsC;AAAA,4CAgCvC,GAhCuC,wBAmCvC,GAnCuC,wBAmDvC,GAnDuC,wBAuDvC,GAvDuC,wBAyDvC,GAzDuC,wBA2DvC,GA3DuC;AAAA;;AAAA;AAiC1ChB,YAAAA,OAAO,CAACiB,GAAR,CAAY,iCAAZ;AAjC0C,6CAkCnCF,mBAAmB,CAACH,IAlCe;;AAAA;AAoC1CZ,YAAAA,OAAO,CAACiB,GAAR,CACE,qGADF;AAGMC,YAAAA,gBAvCoC,4BAuCjBH,mBAAmB,CAACI,OAvCH,0DAuCjB,sBAA6BC,QAvCZ;;AAAA,iBAwCtCF,gBAxCsC;AAAA;AAAA;AAAA;;AAAA;AAAA,mBAyCJV,mBAAmB,CAAC;AACtDC,cAAAA,UAAU,YAAKnB,OAAO,CAACC,GAAR,CAAYmB,iBAAjB,iBAD4C;AAEtDJ,cAAAA,GAAG,EAAHA;AAFsD,aAAD,CAzCf;;AAAA;AAyClCe,YAAAA,qBAzCkC;AA6CxCrB,YAAAA,OAAO,CAACiB,GAAR,CAAY,wCAAZ,EAAsDC,gBAAtD;AA7CwC,6CA+CjCG,qBAAqB,CAACT,IAAtB,CAA2BC,QAA3B,CAAoC,QAApC,CA/CiC;;AAAA;AAAA,kBAiDpC,IAAIX,KAAJ,CAAU,qDAAV,CAjDoC;;AAAA;AAAA,kBAoDpC,IAAIA,KAAJ,6CACiCa,mBAAmB,CAACH,IADrD,EApDoC;;AAAA;AAAA,kBAwDpC,IAAIV,KAAJ,CAAU,wCAAV,CAxDoC;;AAAA;AAAA,kBA0DpC,IAAIA,KAAJ,CAAU,oCAAV,CA1DoC;;AAAA;AAAA,kBA4DpC,IAAIA,KAAJ,wCAC4Ba,mBAAmB,CAACH,IADhD,EA5DoC;;AAAA;AAAA,kBAgEpC,IAAIV,KAAJ,uDAC2Cc,UAD3C,gBAC2DD,mBAAmB,CAACH,IAD/E,EAhEoC;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAAH;;AAAA,kBAAhCd,gCAAgC;AAAA;AAAA;AAAA,GAAtC;AAsEP,gBAAsBgB,0BAAtB;AAAA;AAAA;;;yFAAO,kBAA0CR,GAA1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBACAA,GADA;AAAA;AAAA;AAAA;;AAAA,kBAEG,IAAIJ,KAAJ,CAAU,iDAAV,CAFH;;AAAA;AAKLF,YAAAA,OAAO,CAACiB,GAAR,CACE,kEADF,EAEEX,GAFF;AAKMgB,YAAAA,WAVD,GAUe;AAClBC,cAAAA,cAAc,EAAE;AACdC,gBAAAA,IAAI,EAAE;AACJC,kBAAAA,MAAM,EAAE,KADJ;AAEJC,kBAAAA,IAAI,EAAEpB;AAFF;AADQ;AADE,aAVf;AAmBCqB,YAAAA,MAnBD,GAmBU;AACbC,cAAAA,YAAY,EAAEtC,OAAO,CAACC,GAAR,CAAYsC,qBADb;AAEbC,cAAAA,cAAc,EAAE,iBAFH;AAGbC,cAAAA,OAAO,EAAEC,IAAI,CAACC,SAAL,CAAeX,WAAf;AAHI,aAnBV;AAAA;AAAA;AAAA,mBA0BkB7B,MAAM,CAACyC,MAAP,CAAcP,MAAd,EAAsBQ,OAAtB,EA1BlB;;AAAA;AA0BGC,YAAAA,MA1BH;AA4BGC,YAAAA,QA5BH,GA4BcL,IAAI,CAACM,KAAL,CAAWF,MAAM,CAACL,OAAlB,CA5Bd;AAAA,8CA8BIM,QA9BJ;;AAAA;AAAA;AAAA;AAgCGE,YAAAA,YAhCH,wDAgCgE,aAAMC,OAhCtE;AAiCHxC,YAAAA,OAAO,CAACyC,KAAR,CAAcF,YAAd;AAjCG,kBAkCG,IAAIrC,KAAJ,CAAUqC,YAAV,CAlCH;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,G;;;;AAsCP,gBAAsB/B,mBAAtB;AAAA;AAAA;;;iFAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAqCC,YAAAA,UAArC,SAAqCA,UAArC,EAAiDH,GAAjD,SAAiDA,GAAjD;AACLN,YAAAA,OAAO,
|
|
1
|
+
{"version":3,"sources":["../../../src/helpers/fetch-image-for-pdf-generator-service/index.js"],"names":["AWS","REGION","process","env","AWS_REGION","lambda","Lambda","region","s3","S3","fetchImageForPdfGeneratorService","url","console","info","Error","urlMatch","match","applicationId","key","split","fetchResourceFromS3","bucketName","S3_BUCKET_UPLOADS","alreadyTransformedImage","body","toString","requestImageTransformation","transformerResponse","statusCode","log","redirectLocation","headers","Location","newlyTransformedImage","lambdaEvent","requestContext","http","method","path","params","FunctionName","IMAGE_TRANSFORMER_ARN","InvocationType","Payload","JSON","stringify","invoke","promise","result","response","parse","errorMessage","message","error","Bucket","Key","getObject","Body","contentType","ContentType","contentLength","ContentLength","lastModified","LastModified","etag","ETag","code"],"mappings":";;AAAA,OAAOA,GAAP,MAAgB,SAAhB;AAEA,IAAMC,MAAM,GAAGC,OAAO,CAACC,GAAR,CAAYC,UAA3B;AAEA,IAAMC,MAAM,GAAG,IAAIL,GAAG,CAACM,MAAR,CAAe;AAC5BC,EAAAA,MAAM,EAAEN;AADoB,CAAf,CAAf;AAGA,IAAMO,EAAE,GAAG,IAAIR,GAAG,CAACS,EAAR,CAAW;AACpBF,EAAAA,MAAM,EAAEN;AADY,CAAX,CAAX;AAIA,OAAO,IAAMS,gCAAgC;AAAA,sEAAG,iBAAeC,GAAf;AAAA;;AAAA;AAAA;AAAA;AAAA;AAC9CC,YAAAA,OAAO,CAACC,IAAR,CACE,oEADF;;AAD8C,gBAKzCF,GALyC;AAAA;AAAA;AAAA;;AAAA,kBAMtC,IAAIG,KAAJ,CAAU,0DAAV,CANsC;;AAAA;AASxCC,YAAAA,QATwC,GAS7BJ,GAAG,IAAIA,GAAG,CAACK,KAAJ,CAAU,kBAAV,CATsB;AAUxCC,YAAAA,aAVwC,GAUxBF,QAAQ,IAAIA,QAAQ,CAAC,CAAD,CAVI;;AAAA,gBAYzCE,aAZyC;AAAA;AAAA;AAAA;;AAAA,kBAatC,IAAIH,KAAJ,CAAU,wCAAV,CAbsC;;AAAA;AAgBxCI,YAAAA,GAhBwC,GAgBlCP,GAAG,CAACQ,KAAJ,CAAUF,aAAV,EAAyB,CAAzB,CAhBkC;AAAA;AAAA,mBAkBRG,mBAAmB,CAAC;AACxDC,cAAAA,UAAU,YAAKnB,OAAO,CAACC,GAAR,CAAYmB,iBAAjB,iBAD8C;AAExDJ,cAAAA,GAAG,EAAHA;AAFwD,aAAD,CAlBX;;AAAA;AAkBxCK,YAAAA,uBAlBwC;;AAAA,kBAuB1CA,uBAAuB,IAAIA,uBAAuB,CAACC,IAvBT;AAAA;AAAA;AAAA;;AAAA,6CAwBrCD,uBAAuB,CAACC,IAAxB,CAA6BC,QAA7B,CAAsC,QAAtC,CAxBqC;;AAAA;AAAA;AAAA,mBA2BZC,0BAA0B,CAACR,GAAD,CA3Bd;;AAAA;AA2BxCS,YAAAA,mBA3BwC;AA6BxCC,YAAAA,UA7BwC,GA6B3BD,mBAAmB,CAACC,UA7BO;AAAA,0BA+BtCA,UA/BsC;AAAA,4CAgCvC,GAhCuC,wBAmCvC,GAnCuC,wBAmDvC,GAnDuC,wBAuDvC,GAvDuC,wBAyDvC,GAzDuC,wBA2DvC,GA3DuC;AAAA;;AAAA;AAiC1ChB,YAAAA,OAAO,CAACiB,GAAR,CAAY,iCAAZ;AAjC0C,6CAkCnCF,mBAAmB,CAACH,IAlCe;;AAAA;AAoC1CZ,YAAAA,OAAO,CAACiB,GAAR,CACE,qGADF;AAGMC,YAAAA,gBAvCoC,4BAuCjBH,mBAAmB,CAACI,OAvCH,0DAuCjB,sBAA6BC,QAvCZ;;AAAA,iBAwCtCF,gBAxCsC;AAAA;AAAA;AAAA;;AAAA;AAAA,mBAyCJV,mBAAmB,CAAC;AACtDC,cAAAA,UAAU,YAAKnB,OAAO,CAACC,GAAR,CAAYmB,iBAAjB,iBAD4C;AAEtDJ,cAAAA,GAAG,EAAHA;AAFsD,aAAD,CAzCf;;AAAA;AAyClCe,YAAAA,qBAzCkC;AA6CxCrB,YAAAA,OAAO,CAACiB,GAAR,CAAY,wCAAZ,EAAsDC,gBAAtD;AA7CwC,6CA+CjCG,qBAAqB,CAACT,IAAtB,CAA2BC,QAA3B,CAAoC,QAApC,CA/CiC;;AAAA;AAAA,kBAiDpC,IAAIX,KAAJ,CAAU,qDAAV,CAjDoC;;AAAA;AAAA,kBAoDpC,IAAIA,KAAJ,6CACiCa,mBAAmB,CAACH,IADrD,EApDoC;;AAAA;AAAA,kBAwDpC,IAAIV,KAAJ,CAAU,wCAAV,CAxDoC;;AAAA;AAAA,kBA0DpC,IAAIA,KAAJ,CAAU,oCAAV,CA1DoC;;AAAA;AAAA,kBA4DpC,IAAIA,KAAJ,wCAC4Ba,mBAAmB,CAACH,IADhD,EA5DoC;;AAAA;AAAA,kBAgEpC,IAAIV,KAAJ,uDAC2Cc,UAD3C,gBAC2DD,mBAAmB,CAACH,IAD/E,EAhEoC;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAAH;;AAAA,kBAAhCd,gCAAgC;AAAA;AAAA;AAAA,GAAtC;AAsEP,gBAAsBgB,0BAAtB;AAAA;AAAA;;;yFAAO,kBAA0CR,GAA1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBACAA,GADA;AAAA;AAAA;AAAA;;AAAA,kBAEG,IAAIJ,KAAJ,CAAU,iDAAV,CAFH;;AAAA;AAKLF,YAAAA,OAAO,CAACiB,GAAR,CACE,kEADF,EAEEX,GAFF;AAKMgB,YAAAA,WAVD,GAUe;AAClBC,cAAAA,cAAc,EAAE;AACdC,gBAAAA,IAAI,EAAE;AACJC,kBAAAA,MAAM,EAAE,KADJ;AAEJC,kBAAAA,IAAI,EAAEpB;AAFF;AADQ;AADE,aAVf;AAmBCqB,YAAAA,MAnBD,GAmBU;AACbC,cAAAA,YAAY,EAAEtC,OAAO,CAACC,GAAR,CAAYsC,qBADb;AAEbC,cAAAA,cAAc,EAAE,iBAFH;AAGbC,cAAAA,OAAO,EAAEC,IAAI,CAACC,SAAL,CAAeX,WAAf;AAHI,aAnBV;AAAA;AAAA;AAAA,mBA0BkB7B,MAAM,CAACyC,MAAP,CAAcP,MAAd,EAAsBQ,OAAtB,EA1BlB;;AAAA;AA0BGC,YAAAA,MA1BH;AA4BGC,YAAAA,QA5BH,GA4BcL,IAAI,CAACM,KAAL,CAAWF,MAAM,CAACL,OAAlB,CA5Bd;AAAA,8CA8BIM,QA9BJ;;AAAA;AAAA;AAAA;AAgCGE,YAAAA,YAhCH,wDAgCgE,aAAMC,OAhCtE;AAiCHxC,YAAAA,OAAO,CAACyC,KAAR,CAAcF,YAAd;AAjCG,kBAkCG,IAAIrC,KAAJ,CAAUqC,YAAV,CAlCH;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,G;;;;AAsCP,gBAAsB/B,mBAAtB;AAAA;AAAA;;;iFAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAqCC,YAAAA,UAArC,SAAqCA,UAArC,EAAiDH,GAAjD,SAAiDA,GAAjD;AACLN,YAAAA,OAAO,CAACC,IAAR,8CACwCQ,UADxC,yBACiEH,GADjE;;AADK,kBAID,CAACG,UAAD,IAAe,CAACH,GAJf;AAAA;AAAA;AAAA;;AAAA,kBAKG,IAAIJ,KAAJ,CACJ,2DACE8B,IAAI,CAACC,SAAL,CAAe;AAAExB,cAAAA,UAAU,EAAVA,UAAF;AAAcH,cAAAA,GAAG,EAAHA;AAAd,aAAf,CAFE,CALH;;AAAA;AAWCqB,YAAAA,MAXD,GAWU;AACbe,cAAAA,MAAM,EAAEjC,UADK;AAEbkC,cAAAA,GAAG,EAAErC;AAFQ,aAXV;AAAA;AAAA;AAAA,mBAiBkBV,EAAE,CAACgD,SAAH,CAAajB,MAAb,EAAqBQ,OAArB,EAjBlB;;AAAA;AAiBGC,YAAAA,MAjBH;AAAA,8CAmBI;AACLxB,cAAAA,IAAI,EAAEwB,MAAM,CAACS,IADR;AAELC,cAAAA,WAAW,EAAEV,MAAM,CAACW,WAFf;AAGLC,cAAAA,aAAa,EAAEZ,MAAM,CAACa,aAHjB;AAILC,cAAAA,YAAY,EAAEd,MAAM,CAACe,YAJhB;AAKLC,cAAAA,IAAI,EAAEhB,MAAM,CAACiB;AALR,aAnBJ;;AAAA;AAAA;AAAA;;AAAA,kBA2BC,aAAMC,IAAN,KAAe,WA3BhB;AAAA;AAAA;AAAA;;AAAA,kBA4BK,IAAIpD,KAAJ,kCAAoCO,UAApC,cAAkDH,GAAlD,EA5BL;;AAAA;AA+BHN,YAAAA,OAAO,CAACC,IAAR,CAAa,6DAAb;;AA/BG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,G","sourcesContent":["import AWS from 'aws-sdk'\n\nconst REGION = process.env.AWS_REGION\n\nconst lambda = new AWS.Lambda({\n region: REGION,\n})\nconst s3 = new AWS.S3({\n region: REGION,\n})\n\nexport const fetchImageForPdfGeneratorService = async function(url) {\n console.info(\n 'Fetching image via CloudFront For Serverless Pdf Generator Service'\n )\n\n if (!url) {\n throw new Error('URL is required to fetch image for PDF generator service')\n }\n\n const urlMatch = url && url.match(/([a-f0-9]{24})\\//)\n const applicationId = urlMatch && urlMatch[1]\n\n if (!applicationId) {\n throw new Error('Requestor has insufficient permissions')\n }\n\n const key = url.split(applicationId)[1]\n\n const alreadyTransformedImage = await fetchResourceFromS3({\n bucketName: `${process.env.S3_BUCKET_UPLOADS}-transformed`,\n key,\n })\n\n if (alreadyTransformedImage && alreadyTransformedImage.body) {\n return alreadyTransformedImage.body.toString('base64')\n }\n\n const transformerResponse = await requestImageTransformation(key)\n\n const statusCode = transformerResponse.statusCode\n\n switch (statusCode) {\n case 200:\n console.log('Image transformation successful')\n return transformerResponse.body\n case 302: {\n console.log(\n 'Image transformation successful but image is too big for lambda delivery, fetching directly from S3'\n )\n const redirectLocation = transformerResponse.headers?.Location\n if (redirectLocation) {\n const newlyTransformedImage = await fetchResourceFromS3({\n bucketName: `${process.env.S3_BUCKET_UPLOADS}-transformed`,\n key,\n })\n console.log('Image successfully fetched from S3 at:', redirectLocation)\n\n return newlyTransformedImage.body.toString('base64')\n }\n throw new Error('Redirect response received but no location provided')\n }\n case 400:\n throw new Error(\n `Bad request to image transformer: ${transformerResponse.body}`\n )\n case 403:\n throw new Error('Requested transformed image is too big')\n case 404:\n throw new Error('The requested image does not exist')\n case 500:\n throw new Error(\n `Image transformation failed: ${transformerResponse.body}`\n )\n default:\n throw new Error(\n `Unexpected response from image transformer: ${statusCode} - ${transformerResponse.body}`\n )\n }\n}\n\nexport async function requestImageTransformation(key) {\n if (!key) {\n throw new Error('Image Path is required for image transformation')\n }\n\n console.log(\n 'ImageTransformation: Invoking image transformer lambda for path:',\n key\n )\n\n const lambdaEvent = {\n requestContext: {\n http: {\n method: 'GET',\n path: key,\n },\n },\n }\n\n const params = {\n FunctionName: process.env.IMAGE_TRANSFORMER_ARN,\n InvocationType: 'RequestResponse',\n Payload: JSON.stringify(lambdaEvent),\n }\n\n try {\n const result = await lambda.invoke(params).promise()\n\n const response = JSON.parse(result.Payload)\n\n return response\n } catch (error) {\n const errorMessage = `Failed to invoke image transformer lambda: ${error.message}`\n console.error(errorMessage, error)\n throw new Error(errorMessage)\n }\n}\n\nexport async function fetchResourceFromS3({ bucketName, key }) {\n console.info(\n `Fetching resource from S3 Bucket: '${bucketName}' at path: '${key}'`\n )\n if (!bucketName || !key) {\n throw new Error(\n 'bucketName and key are required for S3 resource fetch ' +\n JSON.stringify({ bucketName, key })\n )\n }\n\n const params = {\n Bucket: bucketName,\n Key: key,\n }\n\n try {\n const result = await s3.getObject(params).promise()\n\n return {\n body: result.Body,\n contentType: result.ContentType,\n contentLength: result.ContentLength,\n lastModified: result.LastModified,\n etag: result.ETag,\n }\n } catch (error) {\n if (error.code !== 'NoSuchKey') {\n throw new Error(`Failed to fetch image: ${bucketName}/${key}`)\n }\n\n console.info('Image not found in transformed bucket, invoking transformer')\n }\n}\n"],"file":"index.js"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
2
|
+
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
3
|
+
import _typeof from "@babel/runtime/helpers/typeof";
|
|
4
|
+
import { imageNotFound } from '../../images';
|
|
5
|
+
import { fetchLighthouseLogo } from '../fetch-lighthouse-logo';
|
|
6
|
+
import { arrayBufferToBase64 } from '../array-buffer-to-base-64';
|
|
7
|
+
import { validateBase64Image } from '../image-validators';
|
|
8
|
+
import fetchPonyfill from 'fetch-ponyfill';
|
|
9
|
+
var contentTypes = {
|
|
10
|
+
'image/png': 'png',
|
|
11
|
+
'image/jpeg': 'jpeg'
|
|
12
|
+
}; // NOTE use the native fetch if it's available in the browser, because the
|
|
13
|
+
// ponyfill (which actually uses the github polyfill) does not support all the
|
|
14
|
+
// same options as native fetch
|
|
15
|
+
|
|
16
|
+
var fetch = (typeof self === "undefined" ? "undefined" : _typeof(self)) === 'object' && self.fetch || fetchPonyfill({
|
|
17
|
+
Promise: Promise
|
|
18
|
+
}).fetch;
|
|
19
|
+
export var fetchImageForWeb = /*#__PURE__*/function () {
|
|
20
|
+
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(url, options) {
|
|
21
|
+
var _options$isHeader, isHeader, Signature, Policy, KeyPairId, firstParamConnector, urlToEncode, encodedUrl, imageResponse, contentLengthHeader, contentType, imageType, logoArrayBuffer, base64Flag, imageStr, base64, isValid;
|
|
22
|
+
|
|
23
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
24
|
+
while (1) {
|
|
25
|
+
switch (_context.prev = _context.next) {
|
|
26
|
+
case 0:
|
|
27
|
+
_options$isHeader = options.isHeader, isHeader = _options$isHeader === void 0 ? false : _options$isHeader, Signature = options.Signature, Policy = options.Policy, KeyPairId = options.KeyPairId;
|
|
28
|
+
_context.prev = 1;
|
|
29
|
+
firstParamConnector = new URL(url).searchParams.size > 0 ? '&' : '?';
|
|
30
|
+
urlToEncode = "".concat(url).concat(firstParamConnector, "Signature=").concat(Signature, "&Policy=").concat(Policy, "&Key-Pair-Id=").concat(KeyPairId);
|
|
31
|
+
encodedUrl = encodeURI(urlToEncode);
|
|
32
|
+
console.info('Fetching image via CloudFront For Web');
|
|
33
|
+
_context.next = 8;
|
|
34
|
+
return fetch(encodedUrl);
|
|
35
|
+
|
|
36
|
+
case 8:
|
|
37
|
+
imageResponse = _context.sent;
|
|
38
|
+
contentLengthHeader = imageResponse.headers.get('content-length');
|
|
39
|
+
contentType = imageResponse.headers.get('content-type');
|
|
40
|
+
|
|
41
|
+
if (!(contentLengthHeader === '0')) {
|
|
42
|
+
_context.next = 13;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return _context.abrupt("return", Promise.reject(new Error("Failed to fetch image as no content length: ".concat(encodedUrl))));
|
|
47
|
+
|
|
48
|
+
case 13:
|
|
49
|
+
if (imageResponse.ok) {
|
|
50
|
+
_context.next = 15;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return _context.abrupt("return", Promise.reject(new Error("Failed to fetch image: ".concat(encodedUrl))));
|
|
55
|
+
|
|
56
|
+
case 15:
|
|
57
|
+
imageType = contentTypes[contentType];
|
|
58
|
+
_context.next = 18;
|
|
59
|
+
return imageResponse.arrayBuffer();
|
|
60
|
+
|
|
61
|
+
case 18:
|
|
62
|
+
logoArrayBuffer = _context.sent;
|
|
63
|
+
base64Flag = "data:image/".concat(imageType, ";base64,");
|
|
64
|
+
imageStr = arrayBufferToBase64(logoArrayBuffer);
|
|
65
|
+
base64 = "".concat(base64Flag).concat(imageStr);
|
|
66
|
+
isValid = validateBase64Image(base64);
|
|
67
|
+
|
|
68
|
+
if (!isValid) {
|
|
69
|
+
_context.next = 25;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return _context.abrupt("return", base64);
|
|
74
|
+
|
|
75
|
+
case 25:
|
|
76
|
+
_context.next = 34;
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case 27:
|
|
80
|
+
_context.prev = 27;
|
|
81
|
+
_context.t0 = _context["catch"](1);
|
|
82
|
+
|
|
83
|
+
if (!isHeader) {
|
|
84
|
+
_context.next = 32;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// NOTE: Replace failed headers with LH logo
|
|
89
|
+
console.error('FetchImageHeaderError', _context.t0);
|
|
90
|
+
return _context.abrupt("return", fetchLighthouseLogo());
|
|
91
|
+
|
|
92
|
+
case 32:
|
|
93
|
+
console.error(_context.t0);
|
|
94
|
+
return _context.abrupt("return", imageNotFound);
|
|
95
|
+
|
|
96
|
+
case 34:
|
|
97
|
+
return _context.abrupt("return", Promise.reject(new Error('InvalidImageError')));
|
|
98
|
+
|
|
99
|
+
case 35:
|
|
100
|
+
case "end":
|
|
101
|
+
return _context.stop();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}, _callee, null, [[1, 27]]);
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
return function fetchImageForWeb(_x, _x2) {
|
|
108
|
+
return _ref.apply(this, arguments);
|
|
109
|
+
};
|
|
110
|
+
}();
|
|
111
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/helpers/fetch-image-for-web/index.js"],"names":["imageNotFound","fetchLighthouseLogo","arrayBufferToBase64","validateBase64Image","fetchPonyfill","contentTypes","fetch","self","Promise","fetchImageForWeb","url","options","isHeader","Signature","Policy","KeyPairId","firstParamConnector","URL","searchParams","size","urlToEncode","encodedUrl","encodeURI","console","info","imageResponse","contentLengthHeader","headers","get","contentType","reject","Error","ok","imageType","arrayBuffer","logoArrayBuffer","base64Flag","imageStr","base64","isValid","error"],"mappings":"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","sourcesContent":["import { imageNotFound } from '../../images'\nimport { fetchLighthouseLogo } from '../fetch-lighthouse-logo'\nimport { arrayBufferToBase64 } from '../array-buffer-to-base-64'\nimport { validateBase64Image } from '../image-validators'\nimport fetchPonyfill from 'fetch-ponyfill'\nconst contentTypes = {\n 'image/png': 'png',\n 'image/jpeg': 'jpeg',\n}\n\n// NOTE use the native fetch if it's available in the browser, because the\n// ponyfill (which actually uses the github polyfill) does not support all the\n// same options as native fetch\nconst fetch =\n (typeof self === 'object' && self.fetch) || fetchPonyfill({ Promise }).fetch\n\nexport const fetchImageForWeb = async function(url, options) {\n const { isHeader = false, Signature, Policy, KeyPairId } = options\n\n try {\n const firstParamConnector = new URL(url).searchParams.size > 0 ? '&' : '?'\n const urlToEncode = `${url}${firstParamConnector}Signature=${Signature}&Policy=${Policy}&Key-Pair-Id=${KeyPairId}`\n\n const encodedUrl = encodeURI(urlToEncode)\n\n console.info('Fetching image via CloudFront For Web')\n\n const imageResponse = await fetch(encodedUrl)\n\n const contentLengthHeader = imageResponse.headers.get('content-length')\n const contentType = imageResponse.headers.get('content-type')\n\n if (contentLengthHeader === '0') {\n return Promise.reject(\n new Error(`Failed to fetch image as no content length: ${encodedUrl}`)\n )\n }\n\n if (!imageResponse.ok) {\n return Promise.reject(new Error(`Failed to fetch image: ${encodedUrl}`))\n }\n\n const imageType = contentTypes[contentType]\n\n const logoArrayBuffer = await imageResponse.arrayBuffer()\n\n const base64Flag = `data:image/${imageType};base64,`\n const imageStr = arrayBufferToBase64(logoArrayBuffer)\n\n const base64 = `${base64Flag}${imageStr}`\n const isValid = validateBase64Image(base64)\n\n if (isValid) {\n return base64\n }\n } catch (error) {\n if (isHeader) {\n // NOTE: Replace failed headers with LH logo\n console.error('FetchImageHeaderError', error)\n return fetchLighthouseLogo()\n }\n\n console.error(error)\n return imageNotFound\n }\n\n return Promise.reject(new Error('InvalidImageError'))\n}\n"],"file":"index.js"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
2
|
+
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
3
|
+
import _typeof from "@babel/runtime/helpers/typeof";
|
|
4
|
+
import { LIGHTHOUSE_LOGO_URL } from '../../constants';
|
|
5
|
+
import { arrayBufferToBase64 } from '../array-buffer-to-base-64';
|
|
6
|
+
import { validateBase64Image } from '../image-validators';
|
|
7
|
+
import fetchPonyfill from 'fetch-ponyfill'; // NOTE use the native fetch if it's available in the browser, because the
|
|
8
|
+
// ponyfill (which actually uses the github polyfill) does not support all the
|
|
9
|
+
// same options as native fetch
|
|
10
|
+
|
|
11
|
+
var fetch = (typeof self === "undefined" ? "undefined" : _typeof(self)) === 'object' && self.fetch || fetchPonyfill({
|
|
12
|
+
Promise: Promise
|
|
13
|
+
}).fetch;
|
|
14
|
+
var contentTypes = {
|
|
15
|
+
'image/png': 'png',
|
|
16
|
+
'image/jpeg': 'jpeg'
|
|
17
|
+
}; // NOTE: This is not stored where other images are - so it cannot go through CloudFront
|
|
18
|
+
|
|
19
|
+
export var fetchLighthouseLogo = /*#__PURE__*/function () {
|
|
20
|
+
var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
|
|
21
|
+
var encodedLogoUrl, logoResponse, contentLengthHeader, contentType, imageType, logoArrayBuffer, base64Flag, imageStr, base64, isValid;
|
|
22
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
23
|
+
while (1) {
|
|
24
|
+
switch (_context.prev = _context.next) {
|
|
25
|
+
case 0:
|
|
26
|
+
encodedLogoUrl = encodeURI(LIGHTHOUSE_LOGO_URL);
|
|
27
|
+
_context.next = 3;
|
|
28
|
+
return fetch(encodedLogoUrl);
|
|
29
|
+
|
|
30
|
+
case 3:
|
|
31
|
+
logoResponse = _context.sent;
|
|
32
|
+
contentLengthHeader = logoResponse.headers.get('content-length');
|
|
33
|
+
contentType = logoResponse.headers.get('content-type');
|
|
34
|
+
|
|
35
|
+
if (!(contentLengthHeader === '0')) {
|
|
36
|
+
_context.next = 8;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return _context.abrupt("return", Promise.reject(new Error("Failed to fetch image as no content length: ".concat(encodedLogoUrl))));
|
|
41
|
+
|
|
42
|
+
case 8:
|
|
43
|
+
if (logoResponse.ok) {
|
|
44
|
+
_context.next = 10;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return _context.abrupt("return", Promise.reject(new Error("Failed to fetch image: ".concat(encodedLogoUrl))));
|
|
49
|
+
|
|
50
|
+
case 10:
|
|
51
|
+
imageType = contentTypes[contentType];
|
|
52
|
+
_context.next = 13;
|
|
53
|
+
return logoResponse.arrayBuffer();
|
|
54
|
+
|
|
55
|
+
case 13:
|
|
56
|
+
logoArrayBuffer = _context.sent;
|
|
57
|
+
base64Flag = "data:image/".concat(imageType, ";base64,");
|
|
58
|
+
imageStr = arrayBufferToBase64(logoArrayBuffer);
|
|
59
|
+
base64 = "".concat(base64Flag).concat(imageStr);
|
|
60
|
+
isValid = validateBase64Image(base64);
|
|
61
|
+
|
|
62
|
+
if (!isValid) {
|
|
63
|
+
_context.next = 20;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return _context.abrupt("return", base64);
|
|
68
|
+
|
|
69
|
+
case 20:
|
|
70
|
+
return _context.abrupt("return", Promise.reject(new Error('InvalidImageError')));
|
|
71
|
+
|
|
72
|
+
case 21:
|
|
73
|
+
case "end":
|
|
74
|
+
return _context.stop();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}, _callee);
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
return function fetchLighthouseLogo() {
|
|
81
|
+
return _ref.apply(this, arguments);
|
|
82
|
+
};
|
|
83
|
+
}();
|
|
84
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/helpers/fetch-lighthouse-logo/index.js"],"names":["LIGHTHOUSE_LOGO_URL","arrayBufferToBase64","validateBase64Image","fetchPonyfill","fetch","self","Promise","contentTypes","fetchLighthouseLogo","encodedLogoUrl","encodeURI","logoResponse","contentLengthHeader","headers","get","contentType","reject","Error","ok","imageType","arrayBuffer","logoArrayBuffer","base64Flag","imageStr","base64","isValid"],"mappings":";;;AAAA,SAASA,mBAAT,QAAoC,iBAApC;AACA,SAASC,mBAAT,QAAoC,4BAApC;AACA,SAASC,mBAAT,QAAoC,qBAApC;AACA,OAAOC,aAAP,MAA0B,gBAA1B,C,CAEA;AACA;AACA;;AACA,IAAMC,KAAK,GACR,QAAOC,IAAP,yCAAOA,IAAP,OAAgB,QAAhB,IAA4BA,IAAI,CAACD,KAAlC,IAA4CD,aAAa,CAAC;AAAEG,EAAAA,OAAO,EAAPA;AAAF,CAAD,CAAb,CAA2BF,KADzE;AAGA,IAAMG,YAAY,GAAG;AACnB,eAAa,KADM;AAEnB,gBAAc;AAFK,CAArB,C,CAKA;;AACA,OAAO,IAAMC,mBAAmB;AAAA,sEAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAC3BC,YAAAA,cAD2B,GACVC,SAAS,CAACV,mBAAD,CADC;AAAA;AAAA,mBAENI,KAAK,CAACK,cAAD,CAFC;;AAAA;AAE3BE,YAAAA,YAF2B;AAI3BC,YAAAA,mBAJ2B,GAILD,YAAY,CAACE,OAAb,CAAqBC,GAArB,CAAyB,gBAAzB,CAJK;AAK3BC,YAAAA,WAL2B,GAKbJ,YAAY,CAACE,OAAb,CAAqBC,GAArB,CAAyB,cAAzB,CALa;;AAAA,kBAO7BF,mBAAmB,KAAK,GAPK;AAAA;AAAA;AAAA;;AAAA,6CAQxBN,OAAO,CAACU,MAAR,CACL,IAAIC,KAAJ,uDAAyDR,cAAzD,EADK,CARwB;;AAAA;AAAA,gBAa5BE,YAAY,CAACO,EAbe;AAAA;AAAA;AAAA;;AAAA,6CAcxBZ,OAAO,CAACU,MAAR,CAAe,IAAIC,KAAJ,kCAAoCR,cAApC,EAAf,CAdwB;;AAAA;AAiB3BU,YAAAA,SAjB2B,GAiBfZ,YAAY,CAACQ,WAAD,CAjBG;AAAA;AAAA,mBAmBHJ,YAAY,CAACS,WAAb,EAnBG;;AAAA;AAmB3BC,YAAAA,eAnB2B;AAqB3BC,YAAAA,UArB2B,wBAqBAH,SArBA;AAsB3BI,YAAAA,QAtB2B,GAsBhBtB,mBAAmB,CAACoB,eAAD,CAtBH;AAwB3BG,YAAAA,MAxB2B,aAwBfF,UAxBe,SAwBFC,QAxBE;AAyB3BE,YAAAA,OAzB2B,GAyBjBvB,mBAAmB,CAACsB,MAAD,CAzBF;;AAAA,iBA2B7BC,OA3B6B;AAAA;AAAA;AAAA;;AAAA,6CA4BxBD,MA5BwB;;AAAA;AAAA,6CA+B1BlB,OAAO,CAACU,MAAR,CAAe,IAAIC,KAAJ,CAAU,mBAAV,CAAf,CA/B0B;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAAH;;AAAA,kBAAnBT,mBAAmB;AAAA;AAAA;AAAA,GAAzB","sourcesContent":["import { LIGHTHOUSE_LOGO_URL } from '../../constants'\nimport { arrayBufferToBase64 } from '../array-buffer-to-base-64'\nimport { validateBase64Image } from '../image-validators'\nimport fetchPonyfill from 'fetch-ponyfill'\n\n// NOTE use the native fetch if it's available in the browser, because the\n// ponyfill (which actually uses the github polyfill) does not support all the\n// same options as native fetch\nconst fetch =\n (typeof self === 'object' && self.fetch) || fetchPonyfill({ Promise }).fetch\n\nconst contentTypes = {\n 'image/png': 'png',\n 'image/jpeg': 'jpeg',\n}\n\n// NOTE: This is not stored where other images are - so it cannot go through CloudFront\nexport const fetchLighthouseLogo = async function() {\n const encodedLogoUrl = encodeURI(LIGHTHOUSE_LOGO_URL)\n const logoResponse = await fetch(encodedLogoUrl)\n\n const contentLengthHeader = logoResponse.headers.get('content-length')\n const contentType = logoResponse.headers.get('content-type')\n\n if (contentLengthHeader === '0') {\n return Promise.reject(\n new Error(`Failed to fetch image as no content length: ${encodedLogoUrl}`)\n )\n }\n\n if (!logoResponse.ok) {\n return Promise.reject(new Error(`Failed to fetch image: ${encodedLogoUrl}`))\n }\n\n const imageType = contentTypes[contentType]\n\n const logoArrayBuffer = await logoResponse.arrayBuffer()\n\n const base64Flag = `data:image/${imageType};base64,`\n const imageStr = arrayBufferToBase64(logoArrayBuffer)\n\n const base64 = `${base64Flag}${imageStr}`\n const isValid = validateBase64Image(base64)\n\n if (isValid) {\n return base64\n }\n\n return Promise.reject(new Error('InvalidImageError'))\n}\n"],"file":"index.js"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function validateBase64Image(base64String) {
|
|
2
|
+
var isJpeg = base64String.startsWith('data:image/jpeg;base64,');
|
|
3
|
+
if (isJpeg) return validateJpegImage(base64String);
|
|
4
|
+
var isPng = base64String.startsWith('data:image/png;base64,');
|
|
5
|
+
if (isPng) return validatePngImage(base64String);
|
|
6
|
+
return false;
|
|
7
|
+
} // See SO for more info: https://stackoverflow.com/a/41635312
|
|
8
|
+
// Fiddle: https://jsfiddle.net/Lnyxuchw/
|
|
9
|
+
|
|
10
|
+
function validateJpegImage(base64string) {
|
|
11
|
+
var src = base64string;
|
|
12
|
+
var imageData = Uint8Array.from(atob(src.replace('data:image/jpeg;base64,', '')), function (c) {
|
|
13
|
+
return c.charCodeAt(0);
|
|
14
|
+
});
|
|
15
|
+
var imageCorrupted = imageData[imageData.length - 1] === 217 && imageData[imageData.length - 2] === 255;
|
|
16
|
+
return imageCorrupted;
|
|
17
|
+
} // See SO for more info: https://stackoverflow.com/a/41635312
|
|
18
|
+
// Fiddle: https://jsfiddle.net/Lnyxuchw/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
function validatePngImage(base64string) {
|
|
22
|
+
var src = base64string;
|
|
23
|
+
var imageData = Uint8Array.from(atob(src.replace('data:image/png;base64,', '')), function (c) {
|
|
24
|
+
return c.charCodeAt(0);
|
|
25
|
+
});
|
|
26
|
+
var sequence = [0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130]; // in hex:
|
|
27
|
+
//check last 12 elements of array so they contains needed values
|
|
28
|
+
|
|
29
|
+
for (var i = 12; i > 0; i--) {
|
|
30
|
+
if (imageData[imageData.length - i] !== sequence[12 - i]) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/helpers/image-validators/index.js"],"names":["validateBase64Image","base64String","isJpeg","startsWith","validateJpegImage","isPng","validatePngImage","base64string","src","imageData","Uint8Array","from","atob","replace","c","charCodeAt","imageCorrupted","length","sequence","i"],"mappings":"AAAA,OAAO,SAASA,mBAAT,CAA6BC,YAA7B,EAA2C;AAChD,MAAMC,MAAM,GAAGD,YAAY,CAACE,UAAb,CAAwB,yBAAxB,CAAf;AAEA,MAAID,MAAJ,EAAY,OAAOE,iBAAiB,CAACH,YAAD,CAAxB;AAEZ,MAAMI,KAAK,GAAGJ,YAAY,CAACE,UAAb,CAAwB,wBAAxB,CAAd;AAEA,MAAIE,KAAJ,EAAW,OAAOC,gBAAgB,CAACL,YAAD,CAAvB;AAEX,SAAO,KAAP;AACD,C,CAED;AACA;;AACA,SAASG,iBAAT,CAA2BG,YAA3B,EAAyC;AACvC,MAAMC,GAAG,GAAGD,YAAZ;AACA,MAAME,SAAS,GAAGC,UAAU,CAACC,IAAX,CAChBC,IAAI,CAACJ,GAAG,CAACK,OAAJ,CAAY,yBAAZ,EAAuC,EAAvC,CAAD,CADY,EAEhB,UAAAC,CAAC;AAAA,WAAIA,CAAC,CAACC,UAAF,CAAa,CAAb,CAAJ;AAAA,GAFe,CAAlB;AAIA,MAAMC,cAAc,GAClBP,SAAS,CAACA,SAAS,CAACQ,MAAV,GAAmB,CAApB,CAAT,KAAoC,GAApC,IACAR,SAAS,CAACA,SAAS,CAACQ,MAAV,GAAmB,CAApB,CAAT,KAAoC,GAFtC;AAIA,SAAOD,cAAP;AACD,C,CAED;AACA;;;AACA,SAASV,gBAAT,CAA0BC,YAA1B,EAAwC;AACtC,MAAMC,GAAG,GAAGD,YAAZ;AACA,MAAME,SAAS,GAAGC,UAAU,CAACC,IAAX,CAChBC,IAAI,CAACJ,GAAG,CAACK,OAAJ,CAAY,wBAAZ,EAAsC,EAAtC,CAAD,CADY,EAEhB,UAAAC,CAAC;AAAA,WAAIA,CAAC,CAACC,UAAF,CAAa,CAAb,CAAJ;AAAA,GAFe,CAAlB;AAIA,MAAMG,QAAQ,GAAG,CAAC,CAAD,EAAI,CAAJ,EAAO,CAAP,EAAU,CAAV,EAAa,EAAb,EAAiB,EAAjB,EAAqB,EAArB,EAAyB,EAAzB,EAA6B,GAA7B,EAAkC,EAAlC,EAAsC,EAAtC,EAA0C,GAA1C,CAAjB,CANsC,CAM0B;AAEhE;;AACA,OAAK,IAAIC,CAAC,GAAG,EAAb,EAAiBA,CAAC,GAAG,CAArB,EAAwBA,CAAC,EAAzB,EAA6B;AAC3B,QAAIV,SAAS,CAACA,SAAS,CAACQ,MAAV,GAAmBE,CAApB,CAAT,KAAoCD,QAAQ,CAAC,KAAKC,CAAN,CAAhD,EAA0D;AACxD,aAAO,KAAP;AACD;AACF;;AAED,SAAO,IAAP;AACD","sourcesContent":["export function validateBase64Image(base64String) {\n const isJpeg = base64String.startsWith('data:image/jpeg;base64,')\n\n if (isJpeg) return validateJpegImage(base64String)\n\n const isPng = base64String.startsWith('data:image/png;base64,')\n\n if (isPng) return validatePngImage(base64String)\n\n return false\n}\n\n// See SO for more info: https://stackoverflow.com/a/41635312\n// Fiddle: https://jsfiddle.net/Lnyxuchw/\nfunction validateJpegImage(base64string) {\n const src = base64string\n const imageData = Uint8Array.from(\n atob(src.replace('data:image/jpeg;base64,', '')),\n c => c.charCodeAt(0)\n )\n const imageCorrupted =\n imageData[imageData.length - 1] === 217 &&\n imageData[imageData.length - 2] === 255\n\n return imageCorrupted\n}\n\n// See SO for more info: https://stackoverflow.com/a/41635312\n// Fiddle: https://jsfiddle.net/Lnyxuchw/\nfunction validatePngImage(base64string) {\n const src = base64string\n const imageData = Uint8Array.from(\n atob(src.replace('data:image/png;base64,', '')),\n c => c.charCodeAt(0)\n )\n const sequence = [0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130] // in hex:\n\n //check last 12 elements of array so they contains needed values\n for (let i = 12; i > 0; i--) {\n if (imageData[imageData.length - i] !== sequence[12 - i]) {\n return false\n }\n }\n\n return true\n}\n"],"file":"index.js"}
|
package/mise.toml
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
[tools]
|
|
2
|
-
node = "20
|
|
2
|
+
node = "20"
|