@lighthouse/common 6.10.0-canary.0 → 6.10.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/CHANGELOG.md +15 -0
- package/dist/errors/FetchImageError.js +19 -0
- package/dist/helpers/build-fetch-url/index.js +26 -1
- package/dist/helpers/fetch-image/index.js +37 -8
- package/dist/helpers/fetch-image-for-pdf-generator-service/index.js +19 -13
- package/dist/helpers/fetch-image-for-web/index.js +36 -5
- package/dist/helpers/validate-url/index.js +15 -0
- package/dist/pdf/helpers/default-header/index.js +11 -1
- package/dist/pdf/helpers/fields/index.js +66 -9
- package/dist/pdf/helpers/generate-definition/index.js +10 -2
- package/dist/scheduling/helpers/generateRepeatingSchedule.js +1 -37
- package/dist/scheduling/helpers/generateScheduleEnd.js +1 -13
- package/dist/scheduling/strategies/getNext.js +0 -25
- package/dist/scheduling/strategies/getNextWeekday.js +0 -31
- package/lib/errors/FetchImageError.js +27 -0
- package/lib/errors/FetchImageError.js.map +1 -0
- package/lib/helpers/build-fetch-url/index.js +26 -1
- package/lib/helpers/build-fetch-url/index.js.map +1 -1
- package/lib/helpers/fetch-image/index.js +39 -7
- package/lib/helpers/fetch-image/index.js.map +1 -1
- package/lib/helpers/fetch-image-for-pdf-generator-service/index.js +50 -32
- package/lib/helpers/fetch-image-for-pdf-generator-service/index.js.map +1 -1
- package/lib/helpers/fetch-image-for-web/index.js +56 -22
- package/lib/helpers/fetch-image-for-web/index.js.map +1 -1
- package/lib/helpers/validate-url/index.js +10 -0
- package/lib/helpers/validate-url/index.js.map +1 -0
- package/lib/pdf/helpers/default-header/index.js +11 -1
- package/lib/pdf/helpers/default-header/index.js.map +1 -1
- package/lib/pdf/helpers/fields/index.js +65 -9
- package/lib/pdf/helpers/fields/index.js.map +1 -1
- package/lib/pdf/helpers/generate-definition/index.js +10 -2
- package/lib/pdf/helpers/generate-definition/index.js.map +1 -1
- package/lib/scheduling/helpers/generateRepeatingSchedule.js +21 -59
- package/lib/scheduling/helpers/generateRepeatingSchedule.js.map +1 -1
- package/lib/scheduling/helpers/generateScheduleEnd.js +1 -13
- package/lib/scheduling/helpers/generateScheduleEnd.js.map +1 -1
- package/lib/scheduling/strategies/getNext.js +4 -31
- package/lib/scheduling/strategies/getNext.js.map +1 -1
- package/lib/scheduling/strategies/getNextWeekday.js +1 -33
- package/lib/scheduling/strategies/getNextWeekday.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# 6.9.3 (Wed Feb 26 2026)
|
|
2
|
+
|
|
3
|
+
#### 🐛 Bug Fix
|
|
4
|
+
|
|
5
|
+
- 🐛 Fix - Cross-region S3/Lambda access for image transformer cache ([@AndrewFinlay](https://github.com/AndrewFinlay))
|
|
6
|
+
- Hardcode IMAGE_CACHE_REGION to us-east-1 for Lambda and S3 clients
|
|
7
|
+
- Fixes NoSuchBucket errors when generating PDFs with images in non-us-east-1 regions
|
|
8
|
+
- Image transformer Lambda and cache bucket only exist in us-east-1
|
|
9
|
+
|
|
10
|
+
#### Authors: 1
|
|
11
|
+
|
|
12
|
+
- Andrew Finlay ([@AndrewFinlay](https://github.com/AndrewFinlay))
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
1
16
|
# 4.27.5 (Fri May 12 2023)
|
|
2
17
|
|
|
3
18
|
#### 🐛 Bug Fix
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.FetchImageError = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Custom error class for image fetching operations
|
|
9
|
+
* Includes context information for better debugging
|
|
10
|
+
*/
|
|
11
|
+
class FetchImageError extends Error {
|
|
12
|
+
constructor(message, context = {}) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'FetchImageError';
|
|
15
|
+
this.context = context;
|
|
16
|
+
this.url = context.url;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.FetchImageError = FetchImageError;
|
|
@@ -4,7 +4,12 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.buildFetchUrl = buildFetchUrl;
|
|
7
|
+
var _index = require("../validate-url/index.js");
|
|
7
8
|
function buildFetchUrl(url, options) {
|
|
9
|
+
// Validate url parameter
|
|
10
|
+
if (!url || typeof url !== 'string') {
|
|
11
|
+
throw new Error(`buildFetchUrl: Invalid url parameter. url=${JSON.stringify(url)}`);
|
|
12
|
+
}
|
|
8
13
|
const {
|
|
9
14
|
awsS3BaseUrl,
|
|
10
15
|
cloudfrontBaseUrl = '',
|
|
@@ -19,6 +24,10 @@ function buildFetchUrl(url, options) {
|
|
|
19
24
|
Signature
|
|
20
25
|
} = options;
|
|
21
26
|
if (shouldUseCloudfront) {
|
|
27
|
+
// Validate cloudfrontBaseUrl when using CloudFront
|
|
28
|
+
if (!cloudfrontBaseUrl) {
|
|
29
|
+
throw new Error(`buildFetchUrl: cloudfrontBaseUrl is required when shouldUseCloudfront=true. cloudfrontBaseUrl=${JSON.stringify(cloudfrontBaseUrl)}`);
|
|
30
|
+
}
|
|
22
31
|
const isWebContext = shouldUseCloudfront && typeof window === 'object';
|
|
23
32
|
const paramMap = {
|
|
24
33
|
width,
|
|
@@ -33,7 +42,18 @@ function buildFetchUrl(url, options) {
|
|
|
33
42
|
}
|
|
34
43
|
const params = Object.entries(paramMap).filter(([, value]) => value != null).map(([key, value]) => `${key}=${String(value)}`);
|
|
35
44
|
const paramsString = params.join('&');
|
|
36
|
-
|
|
45
|
+
const result = `${cloudfrontBaseUrl}/${url}?${paramsString}`;
|
|
46
|
+
|
|
47
|
+
// Validate output is absolute URL
|
|
48
|
+
if (!(0, _index.isAbsoluteUrl)(result)) {
|
|
49
|
+
throw new Error(`buildFetchUrl: Constructed URL is not absolute. cloudfrontBaseUrl=${JSON.stringify(cloudfrontBaseUrl)}, url=${JSON.stringify(url)}, result=${JSON.stringify(result)}`);
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Validate required base URLs for Cloudinary path
|
|
55
|
+
if (!cloudinaryBaseUrl || !awsS3BaseUrl) {
|
|
56
|
+
throw new Error(`buildFetchUrl: cloudinaryBaseUrl and awsS3BaseUrl are required. cloudinaryBaseUrl=${JSON.stringify(cloudinaryBaseUrl)}, awsS3BaseUrl=${JSON.stringify(awsS3BaseUrl)}`);
|
|
37
57
|
}
|
|
38
58
|
const transformations = [];
|
|
39
59
|
let transformationsString = '';
|
|
@@ -43,5 +63,10 @@ function buildFetchUrl(url, options) {
|
|
|
43
63
|
if (fit) transformations.push('c_fit');
|
|
44
64
|
transformationsString = `${transformations.join(',')}/`;
|
|
45
65
|
const fetchUrl = `${cloudinaryBaseUrl}/${transformationsString}${awsS3BaseUrl}/${url}`;
|
|
66
|
+
|
|
67
|
+
// Validate output is absolute URL
|
|
68
|
+
if (!(0, _index.isAbsoluteUrl)(fetchUrl)) {
|
|
69
|
+
throw new Error(`buildFetchUrl: Constructed URL is not absolute. cloudinaryBaseUrl=${JSON.stringify(cloudinaryBaseUrl)}, awsS3BaseUrl=${JSON.stringify(awsS3BaseUrl)}, url=${JSON.stringify(url)}, result=${JSON.stringify(fetchUrl)}`);
|
|
70
|
+
}
|
|
46
71
|
return fetchUrl;
|
|
47
72
|
}
|
|
@@ -15,6 +15,7 @@ var _constants = require("../../constants");
|
|
|
15
15
|
var _images = require("../../images");
|
|
16
16
|
var _fetchImageForPdfGeneratorService = require("../fetch-image-for-pdf-generator-service");
|
|
17
17
|
var _fetchImageForWeb = require("../fetch-image-for-web");
|
|
18
|
+
var _index = require("../validate-url/index.js");
|
|
18
19
|
// NOTE use the native fetch if it's available in the browser, because the
|
|
19
20
|
// ponyfill (which actually uses the github polyfill) does not support all the
|
|
20
21
|
// same options as native fetch
|
|
@@ -37,8 +38,29 @@ const defaultOptions = {
|
|
|
37
38
|
};
|
|
38
39
|
function fetchImage(url, options = {}) {
|
|
39
40
|
const {
|
|
40
|
-
shouldUseCloudfront
|
|
41
|
+
shouldUseCloudfront,
|
|
42
|
+
isHeader = false,
|
|
43
|
+
context = {}
|
|
41
44
|
} = options;
|
|
45
|
+
|
|
46
|
+
// Validate url parameter
|
|
47
|
+
if (!url || typeof url !== 'string') {
|
|
48
|
+
const error = new Error(`fetchImage: Invalid url parameter. url=${JSON.stringify(url)}`);
|
|
49
|
+
if (isHeader) {
|
|
50
|
+
console.error('FetchImageHeaderError', {
|
|
51
|
+
message: error.message,
|
|
52
|
+
url,
|
|
53
|
+
context
|
|
54
|
+
});
|
|
55
|
+
return fetchImage(_constants.LIGHTHOUSE_LOGO_URL, defaultOptions);
|
|
56
|
+
}
|
|
57
|
+
console.error('FetchImageError', {
|
|
58
|
+
message: error.message,
|
|
59
|
+
url,
|
|
60
|
+
context
|
|
61
|
+
});
|
|
62
|
+
return _bluebird.default.resolve(_images.imageNotFound);
|
|
63
|
+
}
|
|
42
64
|
if (shouldUseCloudfront) {
|
|
43
65
|
const isWebContext = typeof window === 'object';
|
|
44
66
|
return isWebContext ?
|
|
@@ -50,9 +72,6 @@ function fetchImage(url, options = {}) {
|
|
|
50
72
|
...defaultOptions,
|
|
51
73
|
...options
|
|
52
74
|
};
|
|
53
|
-
const {
|
|
54
|
-
isHeader = false
|
|
55
|
-
} = options;
|
|
56
75
|
return fetch(encodedUrl, fetchOptions).then(response => {
|
|
57
76
|
const contentHeader = response.headers.get('content-length');
|
|
58
77
|
const contentType = response.headers.get('content-type');
|
|
@@ -60,10 +79,10 @@ function fetchImage(url, options = {}) {
|
|
|
60
79
|
// NOTE: the response will be ok but we won't be able to render any
|
|
61
80
|
// image meaning pdfmake will error. Raise error here and return early.
|
|
62
81
|
if (contentHeader === '0') {
|
|
63
|
-
return _bluebird.default.reject(new Error(`Failed to fetch image as no content length: ${
|
|
82
|
+
return _bluebird.default.reject(new Error(`Failed to fetch image as no content length: ${url}`));
|
|
64
83
|
}
|
|
65
84
|
if (!response.ok) {
|
|
66
|
-
return _bluebird.default.reject(new Error(`Failed to fetch image: ${
|
|
85
|
+
return _bluebird.default.reject(new Error(`Failed to fetch image: ${url}`));
|
|
67
86
|
}
|
|
68
87
|
const imageType = contentTypes[contentType];
|
|
69
88
|
return response.arrayBuffer().then(buffer => ({
|
|
@@ -85,10 +104,20 @@ function fetchImage(url, options = {}) {
|
|
|
85
104
|
}).catch(error => {
|
|
86
105
|
if (isHeader) {
|
|
87
106
|
// NOTE: Replace failed headers with LH logo
|
|
88
|
-
console.error('FetchImageHeaderError',
|
|
107
|
+
console.error('FetchImageHeaderError', {
|
|
108
|
+
url,
|
|
109
|
+
message: error.message,
|
|
110
|
+
context,
|
|
111
|
+
stack: error.stack
|
|
112
|
+
});
|
|
89
113
|
return fetchImage(_constants.LIGHTHOUSE_LOGO_URL, defaultOptions);
|
|
90
114
|
}
|
|
91
|
-
console.error(
|
|
115
|
+
console.error('FetchImageError', {
|
|
116
|
+
url,
|
|
117
|
+
message: error.message,
|
|
118
|
+
context,
|
|
119
|
+
stack: error.stack
|
|
120
|
+
});
|
|
92
121
|
return _images.imageNotFound;
|
|
93
122
|
});
|
|
94
123
|
}
|
|
@@ -10,12 +10,14 @@ exports.formatBase64Image = formatBase64Image;
|
|
|
10
10
|
exports.requestImageTransformation = requestImageTransformation;
|
|
11
11
|
var _imageValidators = require("../image-validators");
|
|
12
12
|
var _awsSdk = _interopRequireDefault(require("aws-sdk"));
|
|
13
|
-
|
|
13
|
+
// Image transformer and cache are always in us-east-1 (shared across all regions)
|
|
14
|
+
// The image-optimisation-image-processing Lambda only exists in us-east-1
|
|
15
|
+
const IMAGE_CACHE_REGION = 'us-east-1';
|
|
14
16
|
const lambda = new _awsSdk.default.Lambda({
|
|
15
|
-
region:
|
|
17
|
+
region: IMAGE_CACHE_REGION
|
|
16
18
|
});
|
|
17
19
|
const s3 = new _awsSdk.default.S3({
|
|
18
|
-
region:
|
|
20
|
+
region: IMAGE_CACHE_REGION
|
|
19
21
|
});
|
|
20
22
|
const fetchImageForPdfGeneratorService = async function (url) {
|
|
21
23
|
console.debug('Fetching image via CloudFront For Serverless Pdf Generator Service');
|
|
@@ -46,6 +48,7 @@ const fetchImageForPdfGeneratorService = async function (url) {
|
|
|
46
48
|
}
|
|
47
49
|
const transformerResponse = await requestImageTransformation(`/${path}`, searchParamsObject);
|
|
48
50
|
const statusCode = transformerResponse.statusCode;
|
|
51
|
+
const redirectLocation = transformerResponse.headers?.Location;
|
|
49
52
|
switch (statusCode) {
|
|
50
53
|
case 200:
|
|
51
54
|
{
|
|
@@ -58,21 +61,24 @@ const fetchImageForPdfGeneratorService = async function (url) {
|
|
|
58
61
|
}
|
|
59
62
|
case 302:
|
|
60
63
|
{
|
|
61
|
-
console.debug('Image transformation successful but image is too big for lambda delivery, fetching directly
|
|
62
|
-
const redirectLocation = transformerResponse.headers.Location;
|
|
64
|
+
console.debug('Image transformation successful but image is too big for lambda delivery, fetching directly via redirect URL');
|
|
63
65
|
if (!redirectLocation) {
|
|
64
66
|
throw new Error('Redirect response received but no location provided');
|
|
65
67
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
let response;
|
|
69
|
+
try {
|
|
70
|
+
response = await fetch(redirectLocation);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
throw new Error(`Failed to fetch image from redirect URL: ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
throw new Error(`Failed to fetch image from redirect: ${response.status}`);
|
|
76
|
+
}
|
|
77
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
78
|
+
return formatBase64Image({
|
|
79
|
+
base64String: buffer.toString('base64'),
|
|
73
80
|
key: transformedBucketImagePath
|
|
74
81
|
});
|
|
75
|
-
return fullDataUrl;
|
|
76
82
|
}
|
|
77
83
|
case 400:
|
|
78
84
|
throw new Error(`Bad request to image transformer: ${transformerResponse.body}`);
|
|
@@ -6,13 +6,34 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.fetchImageForWeb = void 0;
|
|
7
7
|
var _images = require("../../images");
|
|
8
8
|
var _fetchLighthouseLogo = require("../fetch-lighthouse-logo");
|
|
9
|
+
var _index = require("../validate-url/index.js");
|
|
9
10
|
const fetchImageForWeb = async function (url, options) {
|
|
10
11
|
const {
|
|
11
12
|
isHeader = false,
|
|
12
13
|
Signature,
|
|
13
14
|
Policy,
|
|
14
|
-
KeyPairId
|
|
15
|
+
KeyPairId,
|
|
16
|
+
context = {}
|
|
15
17
|
} = options;
|
|
18
|
+
|
|
19
|
+
// Validate url parameter
|
|
20
|
+
if (!url || typeof url !== 'string') {
|
|
21
|
+
const error = new Error(`fetchImageForWeb: Invalid url parameter. url=${JSON.stringify(url)}`);
|
|
22
|
+
if (isHeader) {
|
|
23
|
+
console.error('FetchImageHeaderError', {
|
|
24
|
+
message: error.message,
|
|
25
|
+
url,
|
|
26
|
+
context
|
|
27
|
+
});
|
|
28
|
+
return (0, _fetchLighthouseLogo.fetchLighthouseLogo)();
|
|
29
|
+
}
|
|
30
|
+
console.error('FetchImageError', {
|
|
31
|
+
message: error.message,
|
|
32
|
+
url,
|
|
33
|
+
context
|
|
34
|
+
});
|
|
35
|
+
return _images.imageNotFound;
|
|
36
|
+
}
|
|
16
37
|
try {
|
|
17
38
|
const firstParamConnector = url.indexOf('?') > -1 ? '&' : '?';
|
|
18
39
|
const hasSignatureParams = url.includes('Signature=') && url.includes('Policy=') && url.includes('Key-Pair-Id=');
|
|
@@ -21,19 +42,29 @@ const fetchImageForWeb = async function (url, options) {
|
|
|
21
42
|
const imageResponse = await fetch(constructedUrl);
|
|
22
43
|
const contentLengthHeader = imageResponse.headers.get('content-length');
|
|
23
44
|
if (contentLengthHeader === '0') {
|
|
24
|
-
return Promise.reject(new Error(`Failed to fetch image as no content length: ${
|
|
45
|
+
return Promise.reject(new Error(`Failed to fetch image as no content length: ${url}`));
|
|
25
46
|
}
|
|
26
47
|
if (!imageResponse.ok) {
|
|
27
|
-
return Promise.reject(new Error(`Failed to fetch image: ${
|
|
48
|
+
return Promise.reject(new Error(`Failed to fetch image: ${url}`));
|
|
28
49
|
}
|
|
29
50
|
return await imageResponse.arrayBuffer();
|
|
30
51
|
} catch (error) {
|
|
31
52
|
if (isHeader) {
|
|
32
53
|
// NOTE: Replace failed headers with LH logo
|
|
33
|
-
console.error('FetchImageHeaderError',
|
|
54
|
+
console.error('FetchImageHeaderError', {
|
|
55
|
+
url,
|
|
56
|
+
message: error.message,
|
|
57
|
+
context,
|
|
58
|
+
stack: error.stack
|
|
59
|
+
});
|
|
34
60
|
return (0, _fetchLighthouseLogo.fetchLighthouseLogo)();
|
|
35
61
|
}
|
|
36
|
-
console.error(
|
|
62
|
+
console.error('FetchImageError', {
|
|
63
|
+
url,
|
|
64
|
+
message: error.message,
|
|
65
|
+
context,
|
|
66
|
+
stack: error.stack
|
|
67
|
+
});
|
|
37
68
|
return _images.imageNotFound;
|
|
38
69
|
}
|
|
39
70
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isAbsoluteUrl = isAbsoluteUrl;
|
|
7
|
+
/**
|
|
8
|
+
* Checks if a URL is absolute (starts with http:// or https://)
|
|
9
|
+
* @param {string} url - The URL to validate
|
|
10
|
+
* @returns {boolean} - True if URL is absolute, false otherwise
|
|
11
|
+
*/
|
|
12
|
+
function isAbsoluteUrl(url) {
|
|
13
|
+
if (!url || typeof url !== 'string') return false;
|
|
14
|
+
return url.startsWith('http://') || url.startsWith('https://');
|
|
15
|
+
}
|
|
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.defaultHeader = defaultHeader;
|
|
7
7
|
var _helpers = require("../../../helpers");
|
|
8
|
+
var _constants = require("../../../constants");
|
|
9
|
+
var _index = require("../../../helpers/validate-url/index.js");
|
|
8
10
|
function defaultHeader({
|
|
9
11
|
Signature,
|
|
10
12
|
Policy,
|
|
@@ -19,7 +21,15 @@ function defaultHeader({
|
|
|
19
21
|
timestamp,
|
|
20
22
|
timezone
|
|
21
23
|
});
|
|
22
|
-
|
|
24
|
+
|
|
25
|
+
// Validate logoUrl and use fallback if invalid
|
|
26
|
+
const effectiveLogoUrl = (0, _index.isAbsoluteUrl)(logoUrl) ? logoUrl : _constants.LIGHTHOUSE_LOGO_URL;
|
|
27
|
+
if (!(0, _index.isAbsoluteUrl)(logoUrl)) {
|
|
28
|
+
console.warn('defaultHeader: Invalid logoUrl, using Lighthouse logo', {
|
|
29
|
+
providedLogoUrl: logoUrl
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return (0, _helpers.fetchImage)(effectiveLogoUrl, {
|
|
23
33
|
Signature,
|
|
24
34
|
Policy,
|
|
25
35
|
KeyPairId,
|
|
@@ -13,6 +13,18 @@ var _momentTimezone = _interopRequireDefault(require("moment-timezone"));
|
|
|
13
13
|
var _helpers = require("../../../helpers");
|
|
14
14
|
var _images = require("../../../images");
|
|
15
15
|
var _ = require("../");
|
|
16
|
+
function rewriteS3UrlToCloudfront(url, settings) {
|
|
17
|
+
const {
|
|
18
|
+
shouldUseCloudfront,
|
|
19
|
+
cloudfrontBaseUrl,
|
|
20
|
+
awsS3BaseUrl
|
|
21
|
+
} = settings;
|
|
22
|
+
if (!shouldUseCloudfront || !cloudfrontBaseUrl || !awsS3BaseUrl) return url;
|
|
23
|
+
if (typeof url === 'string' && url.startsWith(awsS3BaseUrl)) {
|
|
24
|
+
return url.replace(awsS3BaseUrl, cloudfrontBaseUrl);
|
|
25
|
+
}
|
|
26
|
+
return url;
|
|
27
|
+
}
|
|
16
28
|
function buildFile({
|
|
17
29
|
file,
|
|
18
30
|
settings,
|
|
@@ -41,7 +53,9 @@ function buildImage(options) {
|
|
|
41
53
|
height = 210,
|
|
42
54
|
settings = {},
|
|
43
55
|
signedAsset,
|
|
44
|
-
width = 210
|
|
56
|
+
width = 210,
|
|
57
|
+
fieldLabel,
|
|
58
|
+
fieldType = 'image'
|
|
45
59
|
} = options;
|
|
46
60
|
const {
|
|
47
61
|
awsS3BaseUrl,
|
|
@@ -71,7 +85,15 @@ function buildImage(options) {
|
|
|
71
85
|
});
|
|
72
86
|
|
|
73
87
|
// NOTE: shouldUseCloudfront, Signature, Policy and KeyPairId from settings are used in this context
|
|
74
|
-
return (0, _helpers.fetchImage)(url,
|
|
88
|
+
return (0, _helpers.fetchImage)(url, {
|
|
89
|
+
...settings,
|
|
90
|
+
context: {
|
|
91
|
+
stage: 'field',
|
|
92
|
+
fieldLabel,
|
|
93
|
+
fieldType,
|
|
94
|
+
filepath
|
|
95
|
+
}
|
|
96
|
+
}).then(base64String => ({
|
|
75
97
|
alignment,
|
|
76
98
|
fit: [width, height],
|
|
77
99
|
image: base64String,
|
|
@@ -121,7 +143,15 @@ function buildSummaryField({
|
|
|
121
143
|
};
|
|
122
144
|
}
|
|
123
145
|
// NOTE: shouldUseCloudfront, Signature, Policy and KeyPairId from settings are used in this context
|
|
124
|
-
|
|
146
|
+
const signatureUrl = rewriteS3UrlToCloudfront(value, settings);
|
|
147
|
+
return (0, _helpers.fetchImage)(signatureUrl, {
|
|
148
|
+
...settings,
|
|
149
|
+
context: {
|
|
150
|
+
stage: 'field',
|
|
151
|
+
fieldLabel: field.label,
|
|
152
|
+
fieldType: 'signature'
|
|
153
|
+
}
|
|
154
|
+
}).then(base64String => {
|
|
125
155
|
return {
|
|
126
156
|
alignment: 'left',
|
|
127
157
|
image: base64String,
|
|
@@ -145,7 +175,9 @@ function buildSummaryField({
|
|
|
145
175
|
height: 140,
|
|
146
176
|
width: 140,
|
|
147
177
|
settings,
|
|
148
|
-
signedAsset: signedAssets && (0, _lodash.isArray)(signedAssets) ? signedAssets[0] : null
|
|
178
|
+
signedAsset: signedAssets && (0, _lodash.isArray)(signedAssets) ? signedAssets[0] : null,
|
|
179
|
+
fieldLabel: field.label,
|
|
180
|
+
fieldType: 'media'
|
|
149
181
|
});
|
|
150
182
|
return image;
|
|
151
183
|
}
|
|
@@ -244,7 +276,15 @@ function buildTemplateFieldRow({
|
|
|
244
276
|
if (isSignatureField) {
|
|
245
277
|
if (!value) return [labelText, ''];
|
|
246
278
|
// NOTE: shouldUseCloudfront, Signature, Policy and KeyPairId from settings are used in this context
|
|
247
|
-
|
|
279
|
+
const signatureUrl = rewriteS3UrlToCloudfront(value, settings);
|
|
280
|
+
return (0, _helpers.fetchImage)(signatureUrl, {
|
|
281
|
+
...settings,
|
|
282
|
+
context: {
|
|
283
|
+
stage: 'field',
|
|
284
|
+
fieldLabel: label,
|
|
285
|
+
fieldType: 'signature'
|
|
286
|
+
}
|
|
287
|
+
}).then(base64String => {
|
|
248
288
|
const values = {
|
|
249
289
|
alignment: 'left',
|
|
250
290
|
image: base64String,
|
|
@@ -254,22 +294,39 @@ function buildTemplateFieldRow({
|
|
|
254
294
|
});
|
|
255
295
|
}
|
|
256
296
|
if (isDisplayImageField) {
|
|
257
|
-
|
|
297
|
+
// Guard against nullish values
|
|
298
|
+
if (!value) {
|
|
299
|
+
return [[], {}];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Ensure value is always treated as an array of filepaths
|
|
303
|
+
const filepaths = (0, _lodash.isArray)(value) ? value : [value];
|
|
304
|
+
return _bluebird.default.map(filepaths, (filepath, index) => {
|
|
258
305
|
const signedAssetValue = (0, _lodash.isArray)(signedAssets) ? signedAssets[index] : signedAssets;
|
|
259
306
|
return buildImage({
|
|
260
307
|
filepath,
|
|
261
308
|
settings,
|
|
262
|
-
signedAsset: signedAssetValue
|
|
309
|
+
signedAsset: signedAssetValue,
|
|
310
|
+
fieldLabel: label,
|
|
311
|
+
fieldType: 'image-display'
|
|
263
312
|
});
|
|
264
313
|
}).then(fieldImages => [fieldImages, {}]);
|
|
265
314
|
}
|
|
266
315
|
if (isPhotoField) {
|
|
316
|
+
// Guard against nullish values
|
|
317
|
+
if (!value || (0, _lodash.isArray)(value) && value.length === 0) {
|
|
318
|
+
return [labelText, {
|
|
319
|
+
text: ''
|
|
320
|
+
}];
|
|
321
|
+
}
|
|
267
322
|
return _bluebird.default.map(value, (filepath, index) => {
|
|
268
323
|
const signedAssetValue = (0, _lodash.isArray)(signedAssets) ? signedAssets[index] : signedAssets;
|
|
269
324
|
return buildImage({
|
|
270
325
|
filepath,
|
|
271
326
|
settings,
|
|
272
|
-
signedAsset: signedAssetValue
|
|
327
|
+
signedAsset: signedAssetValue,
|
|
328
|
+
fieldLabel: label,
|
|
329
|
+
fieldType: 'media'
|
|
273
330
|
});
|
|
274
331
|
}).then(fieldImages => {
|
|
275
332
|
const tables = !(0, _lodash.isEmpty)(fieldImages) ? (0, _.imageTables)(fieldImages) : [];
|
|
@@ -298,7 +355,7 @@ function buildTemplateFieldRow({
|
|
|
298
355
|
}
|
|
299
356
|
if (isFileField) {
|
|
300
357
|
const fileList = (0, _lodash.map)(value, (file, index) => {
|
|
301
|
-
const signedAsset = signedAssets[index];
|
|
358
|
+
const signedAsset = signedAssets?.[index];
|
|
302
359
|
return buildFile({
|
|
303
360
|
file,
|
|
304
361
|
settings,
|
|
@@ -52,8 +52,16 @@ function generateDefinition(options) {
|
|
|
52
52
|
pageOrientation,
|
|
53
53
|
pageSize,
|
|
54
54
|
styles: pdfStyles
|
|
55
|
-
})).catch(
|
|
56
|
-
|
|
55
|
+
})).catch(error => {
|
|
56
|
+
// Preserve structured error information for better debugging
|
|
57
|
+
console.error('GenerateDefinitionError', {
|
|
58
|
+
message: error.message,
|
|
59
|
+
context: error.context,
|
|
60
|
+
url: error.url,
|
|
61
|
+
type: options.type,
|
|
62
|
+
stack: error.stack
|
|
63
|
+
});
|
|
64
|
+
throw error; // Preserve original error instead of wrapping
|
|
57
65
|
});
|
|
58
66
|
}
|
|
59
67
|
function pdfHeader(header) {
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
3
|
Object.defineProperty(exports, "__esModule", {
|
|
5
4
|
value: true
|
|
6
5
|
});
|
|
7
6
|
exports.complyingServiceIntervalUnits = void 0;
|
|
8
7
|
exports.generateRepeatingSchedule = generateRepeatingSchedule;
|
|
9
8
|
var _fp = require("lodash/fp");
|
|
10
|
-
var _momentTimezone = _interopRequireDefault(require("moment-timezone"));
|
|
11
9
|
var _ = require(".");
|
|
12
10
|
var _scheduling = require("../scheduling.types");
|
|
13
11
|
var _generators = require("../generators");
|
|
@@ -49,17 +47,7 @@ function* generateRepeatingSchedule(props) {
|
|
|
49
47
|
end,
|
|
50
48
|
start
|
|
51
49
|
});
|
|
52
|
-
|
|
53
|
-
start: _momentTimezone.default.tz(start, timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
54
|
-
end: _momentTimezone.default.tz(end, timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
55
|
-
isStartAndEndValid,
|
|
56
|
-
frequencyUnit,
|
|
57
|
-
timezone
|
|
58
|
-
});
|
|
59
|
-
if (!isStartAndEndValid) {
|
|
60
|
-
console.log('[generateRepeatingSchedule] INVALID START/END - RETURNING EARLY');
|
|
61
|
-
return [];
|
|
62
|
-
}
|
|
50
|
+
if (!isStartAndEndValid) return [];
|
|
63
51
|
const serviceIntervalSequence = (0, _generators.serviceIntervalsGenerator)({
|
|
64
52
|
end,
|
|
65
53
|
serviceHours,
|
|
@@ -70,20 +58,7 @@ function* generateRepeatingSchedule(props) {
|
|
|
70
58
|
// all service intervals otherwise service interval is from the start and end
|
|
71
59
|
const complyToServiceHours = (0, _fp.includes)(frequencyUnit, complyingServiceIntervalUnits);
|
|
72
60
|
const serviceIntervals = complyToServiceHours ? [...serviceIntervalSequence] : [[start, end]];
|
|
73
|
-
console.log('[generateRepeatingSchedule] SERVICE_INTERVALS:', {
|
|
74
|
-
count: serviceIntervals.length,
|
|
75
|
-
complyToServiceHours,
|
|
76
|
-
intervals: serviceIntervals.slice(0, 3).map(interval => ({
|
|
77
|
-
start: _momentTimezone.default.tz(interval[0], timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
78
|
-
end: _momentTimezone.default.tz(interval[1], timezone).format('YYYY-MM-DD HH:mm:ss Z')
|
|
79
|
-
}))
|
|
80
|
-
});
|
|
81
|
-
let serviceIntervalIndex = 0;
|
|
82
61
|
for (const serviceInterval of serviceIntervals) {
|
|
83
|
-
console.log(`[generateRepeatingSchedule] PROCESSING_SERVICE_INTERVAL[${serviceIntervalIndex}]:`, {
|
|
84
|
-
start: _momentTimezone.default.tz(serviceInterval[0], timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
85
|
-
end: _momentTimezone.default.tz(serviceInterval[1], timezone).format('YYYY-MM-DD HH:mm:ss Z')
|
|
86
|
-
});
|
|
87
62
|
yield {
|
|
88
63
|
interval: serviceInterval,
|
|
89
64
|
type: _scheduling.IntervalTypes.Service
|
|
@@ -94,27 +69,16 @@ function* generateRepeatingSchedule(props) {
|
|
|
94
69
|
strategy,
|
|
95
70
|
timezone
|
|
96
71
|
});
|
|
97
|
-
let occurrenceCount = 0;
|
|
98
72
|
for (const occurrenceInterval of occurrenceIntervalsSequence) {
|
|
99
|
-
console.log(`[generateRepeatingSchedule] OCCURRENCE[${occurrenceCount}]:`, {
|
|
100
|
-
start: _momentTimezone.default.tz(occurrenceInterval[0], timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
101
|
-
end: _momentTimezone.default.tz(occurrenceInterval[1], timezone).format('YYYY-MM-DD HH:mm:ss Z')
|
|
102
|
-
});
|
|
103
73
|
yield {
|
|
104
74
|
interval: occurrenceInterval,
|
|
105
75
|
type: _scheduling.IntervalTypes.Occurrence
|
|
106
76
|
};
|
|
107
|
-
occurrenceCount++;
|
|
108
77
|
}
|
|
109
|
-
console.log(`[generateRepeatingSchedule] SERVICE_INTERVAL[${serviceIntervalIndex}] COMPLETED:`, {
|
|
110
|
-
occurrenceCount
|
|
111
|
-
});
|
|
112
78
|
|
|
113
79
|
// NOTE: we must reset isInitial to true following the first service
|
|
114
80
|
// interval otherwise the occurrence will not start exactly on the next
|
|
115
81
|
// service interval start time
|
|
116
82
|
isInitial = true;
|
|
117
|
-
serviceIntervalIndex++;
|
|
118
83
|
}
|
|
119
|
-
console.log('[generateRepeatingSchedule] COMPLETE');
|
|
120
84
|
}
|
|
@@ -26,18 +26,6 @@ function generateScheduleEnd({
|
|
|
26
26
|
// NOTE: if frequency unit less than a week we must set end to jump a week
|
|
27
27
|
// plus the interval length of the schedule frequency
|
|
28
28
|
// so that we can ensure that we are within a service interval
|
|
29
|
-
|
|
30
|
-
const end = isFrequencyLessThanWeek ? mStart.add(1, _scheduling.Unit.Hour).valueOf() : mStart.add(frequencyValue * 2, frequencyUnit).valueOf();
|
|
31
|
-
console.log('[generateScheduleEnd] START:', {
|
|
32
|
-
start: _momentTimezone.default.tz(start, timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
33
|
-
frequency: `${frequencyValue}${frequencyUnit}`,
|
|
34
|
-
isFrequencyLessThanWeek,
|
|
35
|
-
timezone
|
|
36
|
-
});
|
|
37
|
-
console.log('[generateScheduleEnd] END CALCULATED:', {
|
|
38
|
-
end: _momentTimezone.default.tz(end, timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
39
|
-
endTimestamp: end,
|
|
40
|
-
durationFromStart: `${(0, _momentTimezone.default)(end).diff((0, _momentTimezone.default)(start), 'hours')} hours`
|
|
41
|
-
});
|
|
29
|
+
const end = isFrequencyLessThanWeek ? mStart.add(2, _scheduling.Unit.Week).valueOf() : mStart.add(frequencyValue * 2, frequencyUnit).valueOf();
|
|
42
30
|
return end;
|
|
43
31
|
}
|
|
@@ -32,16 +32,6 @@ function* getNext({
|
|
|
32
32
|
} = frequency;
|
|
33
33
|
let initial = isInitial;
|
|
34
34
|
let dateCursor = initial ? _momentTimezone.default.tz(start, timezone).valueOf() : _momentTimezone.default.tz(start, timezone).add(1, _scheduling.Unit.Millisecond).valueOf();
|
|
35
|
-
console.log('[getNext] START:', {
|
|
36
|
-
strategy: type,
|
|
37
|
-
start: _momentTimezone.default.tz(start, timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
38
|
-
end: _momentTimezone.default.tz(end, timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
39
|
-
dateCursor: _momentTimezone.default.tz(dateCursor, timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
40
|
-
isInitial,
|
|
41
|
-
frequency: `${frequencyValue}${frequencyUnit}`,
|
|
42
|
-
duration: `${durationValue}${durationUnit}`
|
|
43
|
-
});
|
|
44
|
-
let occurrenceCount = 0;
|
|
45
35
|
while (dateCursor < end) {
|
|
46
36
|
let nextOccurrenceStart;
|
|
47
37
|
let nextOccurrenceEnd;
|
|
@@ -59,27 +49,12 @@ function* getNext({
|
|
|
59
49
|
nextOccurrenceStart = _momentTimezone.default.max(earliestNextOccurrenceStart, nextOccurrenceStartByBackCalculation).valueOf();
|
|
60
50
|
}
|
|
61
51
|
if (nextOccurrenceEnd <= nextOccurrenceStart || nextOccurrenceEnd > end) {
|
|
62
|
-
console.log('[getNext] BOUNDARY CHECK FAILED:', {
|
|
63
|
-
occurrenceCount,
|
|
64
|
-
nextOccurrenceStart: _momentTimezone.default.tz(nextOccurrenceStart, timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
65
|
-
nextOccurrenceEnd: _momentTimezone.default.tz(nextOccurrenceEnd, timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
66
|
-
end: _momentTimezone.default.tz(end, timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
67
|
-
condition: `(${nextOccurrenceEnd} <= ${nextOccurrenceStart}) || (${nextOccurrenceEnd} > ${end})`
|
|
68
|
-
});
|
|
69
52
|
return;
|
|
70
53
|
}
|
|
71
54
|
if (nextOccurrenceStart >= start) {
|
|
72
55
|
const nextOccurrence = [nextOccurrenceStart, nextOccurrenceEnd - 1];
|
|
73
|
-
console.log(`[getNext] OCCURRENCE[${occurrenceCount}]:`, {
|
|
74
|
-
start: _momentTimezone.default.tz(nextOccurrenceStart, timezone).format('YYYY-MM-DD HH:mm:ss Z'),
|
|
75
|
-
end: _momentTimezone.default.tz(nextOccurrenceEnd - 1, timezone).format('YYYY-MM-DD HH:mm:ss Z')
|
|
76
|
-
});
|
|
77
56
|
yield nextOccurrence;
|
|
78
|
-
occurrenceCount++;
|
|
79
57
|
}
|
|
80
58
|
dateCursor = nextOccurrenceEnd;
|
|
81
59
|
}
|
|
82
|
-
console.log('[getNext] COMPLETE:', {
|
|
83
|
-
occurrenceCount
|
|
84
|
-
});
|
|
85
60
|
}
|