b2b-platform-utils 1.1.56 → 1.1.58
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/errorsMap.js +48 -0
- package/optimizeMedia.js +178 -1
- package/package.json +1 -1
package/errorsMap.js
CHANGED
|
@@ -880,6 +880,54 @@ const STATIC_ERRORS = [
|
|
|
880
880
|
httpCode: 503,
|
|
881
881
|
description: "Service is temporarily unavailable.",
|
|
882
882
|
},
|
|
883
|
+
{
|
|
884
|
+
errorCode: 1144,
|
|
885
|
+
errorKey: "promocodeInvalid",
|
|
886
|
+
httpCode: 422,
|
|
887
|
+
description: "Promo code is invalid or cannot be applied.",
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
errorCode: 1145,
|
|
891
|
+
errorKey: "promocodeExpired",
|
|
892
|
+
httpCode: 422,
|
|
893
|
+
description: "Promo code is expired.",
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
errorCode: 1146,
|
|
897
|
+
errorKey: "promocodeUsageLimitReached",
|
|
898
|
+
httpCode: 422,
|
|
899
|
+
description: "Promo code usage limit has been reached.",
|
|
900
|
+
},
|
|
901
|
+
{
|
|
902
|
+
errorCode: 1147,
|
|
903
|
+
errorKey: "promocodeApplicationModeMismatch",
|
|
904
|
+
httpCode: 422,
|
|
905
|
+
description: "Promo code application mode does not match the requested operation.",
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
errorCode: 1148,
|
|
909
|
+
errorKey: "promocodeTargetBonusAlreadyOwned",
|
|
910
|
+
httpCode: 422,
|
|
911
|
+
description: "Target bonus is already owned by the user.",
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
errorCode: 1149,
|
|
915
|
+
errorKey: "promocodeTargetBonusUnavailable",
|
|
916
|
+
httpCode: 422,
|
|
917
|
+
description: "Target bonus is not available for this promo code.",
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
errorCode: 1150,
|
|
921
|
+
errorKey: "promocodeAlreadyApplied",
|
|
922
|
+
httpCode: 422,
|
|
923
|
+
description: "Promo code has already been applied.",
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
errorCode: 1151,
|
|
927
|
+
errorKey: "promocodeNotActive",
|
|
928
|
+
httpCode: 422,
|
|
929
|
+
description: "Promo code is not active.",
|
|
930
|
+
},
|
|
883
931
|
];
|
|
884
932
|
|
|
885
933
|
const STATIC_BY_KEY = Object.fromEntries(
|
package/optimizeMedia.js
CHANGED
|
@@ -82,8 +82,185 @@ async function saveGameImageBufferWEBPWithOptions(fileDataBuffer, options = {})
|
|
|
82
82
|
.toBuffer();
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
const WEBP_CONVERSION_FORMATS = new Set(['jpeg', 'png', 'webp']);
|
|
86
|
+
|
|
87
|
+
function normalizeImageFileType(originalFileType) {
|
|
88
|
+
if (!originalFileType) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let type = String(originalFileType).toLowerCase().replace(/^\./, '');
|
|
93
|
+
|
|
94
|
+
if (type === 'jpg') {
|
|
95
|
+
type = 'jpeg';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return type;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isKnownFormat(fileType) {
|
|
102
|
+
return fileType === 'gif' || fileType === 'avif' || WEBP_CONVERSION_FORMATS.has(fileType);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function hasResizeOptions(options) {
|
|
106
|
+
const { maxWidth, width, height } = options;
|
|
107
|
+
|
|
108
|
+
return width != null || height != null || maxWidth != null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildResizeOptions(options) {
|
|
112
|
+
const {
|
|
113
|
+
maxWidth,
|
|
114
|
+
width,
|
|
115
|
+
height,
|
|
116
|
+
fit = 'inside',
|
|
117
|
+
withoutEnlargement = true
|
|
118
|
+
} = options;
|
|
119
|
+
|
|
120
|
+
const resizeOptions = { fit, withoutEnlargement };
|
|
121
|
+
|
|
122
|
+
if (width != null) {
|
|
123
|
+
resizeOptions.width = width;
|
|
124
|
+
} else if (maxWidth != null) {
|
|
125
|
+
resizeOptions.width = maxWidth;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (height != null) {
|
|
129
|
+
resizeOptions.height = height;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return resizeOptions;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function resizePipelineIfNeeded(pipeline, fileDataBuffer, options) {
|
|
136
|
+
const { resize = 1 } = options;
|
|
137
|
+
|
|
138
|
+
if (hasResizeOptions(options)) {
|
|
139
|
+
return pipeline.resize(buildResizeOptions(options));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (resize !== 1) {
|
|
143
|
+
const metadata = await sharp(fileDataBuffer).metadata();
|
|
144
|
+
const newWidth = Math.round(metadata.width * resize);
|
|
145
|
+
const newHeight = Math.round(metadata.height * resize);
|
|
146
|
+
|
|
147
|
+
return pipeline.resize(newWidth, newHeight);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return pipeline;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function encodeWebpBuffer(fileDataBuffer, options) {
|
|
154
|
+
let pipeline = sharp(fileDataBuffer);
|
|
155
|
+
pipeline = await resizePipelineIfNeeded(pipeline, fileDataBuffer, options);
|
|
156
|
+
|
|
157
|
+
return pipeline
|
|
158
|
+
.webp({
|
|
159
|
+
quality: options.quality,
|
|
160
|
+
lossless: false,
|
|
161
|
+
smartSubsample: options.smartSubsample,
|
|
162
|
+
effort: options.effort,
|
|
163
|
+
alphaQuality: options.alphaQuality
|
|
164
|
+
})
|
|
165
|
+
.toBuffer();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Format-aware image optimization: preserves GIF/AVIF, converts JPEG/PNG/WebP to WebP, safe fallback on failure.
|
|
170
|
+
* @param {Buffer} fileDataBuffer
|
|
171
|
+
* @param {string} originalFileType
|
|
172
|
+
* @param {object} [options]
|
|
173
|
+
* @returns {Promise<{ buffer: Buffer, fileType: string, optimized: boolean }>}
|
|
174
|
+
*/
|
|
175
|
+
async function optimizeImageBufferByFormat(fileDataBuffer, originalFileType, options = {}) {
|
|
176
|
+
const opts = {
|
|
177
|
+
quality: 80,
|
|
178
|
+
resize: 1,
|
|
179
|
+
alphaQuality: 80,
|
|
180
|
+
maxWidth: null,
|
|
181
|
+
width: null,
|
|
182
|
+
height: null,
|
|
183
|
+
fit: 'inside',
|
|
184
|
+
withoutEnlargement: true,
|
|
185
|
+
effort: 5,
|
|
186
|
+
smartSubsample: true,
|
|
187
|
+
...options
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
let normalizedType = normalizeImageFileType(originalFileType);
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
if (normalizedType === 'gif') {
|
|
194
|
+
return { buffer: fileDataBuffer, fileType: 'gif', optimized: false };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!normalizedType || !isKnownFormat(normalizedType)) {
|
|
198
|
+
const metadata = await sharp(fileDataBuffer).metadata();
|
|
199
|
+
const metadataType = normalizeImageFileType(metadata.format);
|
|
200
|
+
|
|
201
|
+
if (metadataType) {
|
|
202
|
+
normalizedType = metadataType;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (normalizedType === 'gif') {
|
|
207
|
+
return { buffer: fileDataBuffer, fileType: 'gif', optimized: false };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (normalizedType === 'avif') {
|
|
211
|
+
try {
|
|
212
|
+
let pipeline = sharp(fileDataBuffer);
|
|
213
|
+
|
|
214
|
+
if (hasResizeOptions(opts)) {
|
|
215
|
+
pipeline = pipeline.resize(buildResizeOptions(opts));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const buffer = await pipeline
|
|
219
|
+
.avif({
|
|
220
|
+
quality: opts.quality,
|
|
221
|
+
effort: opts.effort
|
|
222
|
+
})
|
|
223
|
+
.toBuffer();
|
|
224
|
+
|
|
225
|
+
return { buffer, fileType: 'avif', optimized: true };
|
|
226
|
+
} catch {
|
|
227
|
+
return { buffer: fileDataBuffer, fileType: 'avif', optimized: false };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (WEBP_CONVERSION_FORMATS.has(normalizedType)) {
|
|
232
|
+
try {
|
|
233
|
+
const buffer = await encodeWebpBuffer(fileDataBuffer, opts);
|
|
234
|
+
|
|
235
|
+
return { buffer, fileType: 'webp', optimized: true };
|
|
236
|
+
} catch {
|
|
237
|
+
return { buffer: fileDataBuffer, fileType: normalizedType, optimized: false };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const buffer = await encodeWebpBuffer(fileDataBuffer, opts);
|
|
243
|
+
|
|
244
|
+
return { buffer, fileType: 'webp', optimized: true };
|
|
245
|
+
} catch {
|
|
246
|
+
return {
|
|
247
|
+
buffer: fileDataBuffer,
|
|
248
|
+
fileType: normalizedType || 'bin',
|
|
249
|
+
optimized: false
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
} catch {
|
|
253
|
+
return {
|
|
254
|
+
buffer: fileDataBuffer,
|
|
255
|
+
fileType: normalizedType || normalizeImageFileType(originalFileType) || 'bin',
|
|
256
|
+
optimized: false
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
85
261
|
module.exports = {
|
|
86
262
|
optimizeImageBufferWEBP,
|
|
87
263
|
saveGameImageBufferWEBP,
|
|
88
|
-
saveGameImageBufferWEBPWithOptions
|
|
264
|
+
saveGameImageBufferWEBPWithOptions,
|
|
265
|
+
optimizeImageBufferByFormat
|
|
89
266
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "b2b-platform-utils",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.58",
|
|
4
4
|
"description": "Shared utilities for Node.js microservices: errors map, local cache, logger, numbers, dates, filesystem, media optimization, paginator, slugger, crypto wrapper, sanitize HTML, sorting.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"license": "KingSizer",
|