@paroicms/server-image-cache-engine 0.25.0
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/README.md +3 -0
- package/ddl/image-cache.ddl.sql +32 -0
- package/dist/api.d.ts +6 -0
- package/dist/api.js +23 -0
- package/dist/api.js.map +1 -0
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +7 -0
- package/dist/constants.js.map +1 -0
- package/dist/database/PaCacheDependency.entity.d.ts +4 -0
- package/dist/database/PaCacheDependency.entity.js +30 -0
- package/dist/database/PaCacheDependency.entity.js.map +1 -0
- package/dist/database/PaImageCache.entity.d.ts +13 -0
- package/dist/database/PaImageCache.entity.js +71 -0
- package/dist/database/PaImageCache.entity.js.map +1 -0
- package/dist/database/PaImageCacheDependency.entity.d.ts +4 -0
- package/dist/database/PaImageCacheDependency.entity.js +30 -0
- package/dist/database/PaImageCacheDependency.entity.js.map +1 -0
- package/dist/database/PaImageCacheKey.entity.d.ts +5 -0
- package/dist/database/PaImageCacheKey.entity.js +35 -0
- package/dist/database/PaImageCacheKey.entity.js.map +1 -0
- package/dist/database/image-file-variant.entity.d.ts +13 -0
- package/dist/database/image-file-variant.entity.js +71 -0
- package/dist/database/image-file-variant.entity.js.map +1 -0
- package/dist/database/queries.d.ts +48 -0
- package/dist/database/queries.js +176 -0
- package/dist/database/queries.js.map +1 -0
- package/dist/db-init/db-init.d.ts +10 -0
- package/dist/db-init/db-init.js +35 -0
- package/dist/db-init/db-init.js.map +1 -0
- package/dist/db-init/ddl-migration.d.ts +12 -0
- package/dist/db-init/ddl-migration.js +51 -0
- package/dist/db-init/ddl-migration.js.map +1 -0
- package/dist/default-bo-favicon.d.ts +3 -0
- package/dist/default-bo-favicon.js +107 -0
- package/dist/default-bo-favicon.js.map +1 -0
- package/dist/engine-types.d.ts +45 -0
- package/dist/engine-types.js +3 -0
- package/dist/engine-types.js.map +1 -0
- package/dist/image-cache-engine.d.ts +48 -0
- package/dist/image-cache-engine.js +139 -0
- package/dist/image-cache-engine.js.map +1 -0
- package/dist/image-processor-setup.d.ts +8 -0
- package/dist/image-processor-setup.js +33 -0
- package/dist/image-processor-setup.js.map +1 -0
- package/dist/internal/compute-variant-size.d.ts +11 -0
- package/dist/internal/compute-variant-size.js +91 -0
- package/dist/internal/compute-variant-size.js.map +1 -0
- package/dist/internal/engine-context.d.ts +17 -0
- package/dist/internal/engine-context.js +26 -0
- package/dist/internal/engine-context.js.map +1 -0
- package/dist/internal/internal.types.d.ts +18 -0
- package/dist/internal/internal.types.js +3 -0
- package/dist/internal/internal.types.js.map +1 -0
- package/dist/internal/resizer.d.ts +20 -0
- package/dist/internal/resizer.js +67 -0
- package/dist/internal/resizer.js.map +1 -0
- package/dist/internal/task-processor.d.ts +2 -0
- package/dist/internal/task-processor.js +62 -0
- package/dist/internal/task-processor.js.map +1 -0
- package/dist/original-images-processing.d.ts +21 -0
- package/dist/original-images-processing.js +93 -0
- package/dist/original-images-processing.js.map +1 -0
- package/dist/public-helpers.d.ts +2 -0
- package/dist/public-helpers.js +8 -0
- package/dist/public-helpers.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PromiseToHandle } from "@paroi/async-lib";
|
|
2
|
+
import { AppLog } from "@paroicms/server-lib";
|
|
3
|
+
import { DataSource, EntityManager } from "typeorm";
|
|
4
|
+
import { ImageVariantWithBinary } from "../engine-types";
|
|
5
|
+
import type { CreateImageCacheEngineOptions } from "../image-cache-engine";
|
|
6
|
+
import { TaskData, TaskKey } from "./internal.types";
|
|
7
|
+
export interface VariantEngineContext extends Omit<CreateImageCacheEngineOptions, "appLog"> {
|
|
8
|
+
status: "ready" | "destroyed";
|
|
9
|
+
appLog: AppLog;
|
|
10
|
+
queue: TaskData[];
|
|
11
|
+
pendingTasks: Map<TaskKey, PromiseToHandle<ImageVariantWithBinary>>;
|
|
12
|
+
mainCn: DataSource;
|
|
13
|
+
cn: EntityManager;
|
|
14
|
+
logNextQuery: (count?: number) => void;
|
|
15
|
+
timeToIdleDays?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function createVariantEngineContext(options: CreateImageCacheEngineOptions): Promise<VariantEngineContext>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createVariantEngineContext = void 0;
|
|
4
|
+
const anywhere_lib_1 = require("@paroicms/anywhere-lib");
|
|
5
|
+
const db_init_1 = require("../db-init/db-init");
|
|
6
|
+
async function createVariantEngineContext(options) {
|
|
7
|
+
const appLog = options.appLog ?? console;
|
|
8
|
+
const { mainCn, logNextQuery } = await (0, db_init_1.createOrOpenMainCn)({
|
|
9
|
+
siteName: options.siteName,
|
|
10
|
+
sqliteFile: options.storage.file,
|
|
11
|
+
appLog,
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
...options,
|
|
15
|
+
status: "ready",
|
|
16
|
+
appLog,
|
|
17
|
+
queue: [],
|
|
18
|
+
pendingTasks: new Map(),
|
|
19
|
+
mainCn,
|
|
20
|
+
cn: mainCn.manager,
|
|
21
|
+
logNextQuery,
|
|
22
|
+
timeToIdleDays: options.timeToIdle === undefined ? undefined : (0, anywhere_lib_1.parseTimeWithUnit)(options.timeToIdle).delay,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
exports.createVariantEngineContext = createVariantEngineContext;
|
|
26
|
+
//# sourceMappingURL=engine-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine-context.js","sourceRoot":"","sources":["../../src/internal/engine-context.ts"],"names":[],"mappings":";;;AACA,yDAA2D;AAG3D,gDAAwD;AAgBjD,KAAK,UAAU,0BAA0B,CAC9C,OAAsC;IAEtC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC;IAEzC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,IAAA,4BAAkB,EAAC;QACxD,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI;QAChC,MAAM;KACP,CAAC,CAAC;IAEH,OAAO;QACL,GAAG,OAAO;QACV,MAAM,EAAE,OAAO;QACf,MAAM;QACN,KAAK,EAAE,EAAE;QACT,YAAY,EAAE,IAAI,GAAG,EAAE;QACvB,MAAM;QACN,EAAE,EAAE,MAAM,CAAC,OAAO;QAClB,YAAY;QACZ,cAAc,EACZ,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAA,gCAAiB,EAAC,OAAO,CAAC,UAAU,CAAC,CAAC,KAAK;KAC7F,CAAC;AACJ,CAAC;AAvBD,gEAuBC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CompleteVariantName } from "../engine-types";
|
|
2
|
+
export interface TaskData {
|
|
3
|
+
taskKey: TaskKey;
|
|
4
|
+
variantName: CompleteVariantName;
|
|
5
|
+
imageSize: ImageSize;
|
|
6
|
+
imageUid: string;
|
|
7
|
+
mediaType: string;
|
|
8
|
+
changeMediaTypeTo?: string;
|
|
9
|
+
dependsOnCacheKey: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ImageSize {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Format is: {imageUid}:{completeVariantName}
|
|
17
|
+
*/
|
|
18
|
+
export type TaskKey = `${string}:${CompleteVariantName}`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal.types.js","sourceRoot":"","sources":["../../src/internal/internal.types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { ImageCompressionQualityPolicy } from "@paroicms/anywhere-lib";
|
|
3
|
+
import { ImageVariantWithBinary } from "../engine-types";
|
|
4
|
+
import { ImageSize } from "./internal.types";
|
|
5
|
+
export type UnsavedImageVariantWithBinary = Omit<ImageVariantWithBinary, "lastModified">;
|
|
6
|
+
export declare function resizeImage(options: {
|
|
7
|
+
binaryFile: Buffer;
|
|
8
|
+
imageSize: ImageSize;
|
|
9
|
+
formatTo?: {
|
|
10
|
+
mediaType: string;
|
|
11
|
+
lossless?: boolean;
|
|
12
|
+
};
|
|
13
|
+
qualityPolicy: ImageCompressionQualityPolicy[];
|
|
14
|
+
}): Promise<UnsavedImageVariantWithBinary>;
|
|
15
|
+
export declare function resizeIcoImage(options: {
|
|
16
|
+
binaryFile: Buffer;
|
|
17
|
+
imageSize: ImageSize;
|
|
18
|
+
}): Promise<UnsavedImageVariantWithBinary>;
|
|
19
|
+
export declare function convertSharpFormatToMediaType(format: string): string;
|
|
20
|
+
export declare function getQuality({ width, height }: ImageSize, qualityPolicy?: ImageCompressionQualityPolicy[]): number;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getQuality = exports.convertSharpFormatToMediaType = exports.resizeIcoImage = exports.resizeImage = void 0;
|
|
7
|
+
const sharp_1 = __importDefault(require("sharp"));
|
|
8
|
+
const constants_1 = require("../constants");
|
|
9
|
+
const image_processor_setup_1 = require("../image-processor-setup");
|
|
10
|
+
const icoEndec = require("ico-endec");
|
|
11
|
+
async function resizeImage(options) {
|
|
12
|
+
if (options.formatTo?.mediaType === "image/x-icon")
|
|
13
|
+
return await resizeIcoImage(options);
|
|
14
|
+
const sharpFormat = options.formatTo
|
|
15
|
+
? convertMediaTypeToSharpFormat(options.formatTo.mediaType)
|
|
16
|
+
: "webp";
|
|
17
|
+
const sharpInst = (0, sharp_1.default)(options.binaryFile)
|
|
18
|
+
.resize(options.imageSize)
|
|
19
|
+
.toFormat(sharpFormat, {
|
|
20
|
+
quality: getQuality(options.imageSize),
|
|
21
|
+
lossless: options.formatTo?.lossless,
|
|
22
|
+
});
|
|
23
|
+
const { data, info } = await (0, image_processor_setup_1.sharpQueue)(() => sharpInst.toBuffer({ resolveWithObject: true }));
|
|
24
|
+
return {
|
|
25
|
+
width: info.width,
|
|
26
|
+
height: info.height,
|
|
27
|
+
weightB: info.size,
|
|
28
|
+
mediaType: convertSharpFormatToMediaType(info.format),
|
|
29
|
+
binaryFile: data,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
exports.resizeImage = resizeImage;
|
|
33
|
+
async function resizeIcoImage(options) {
|
|
34
|
+
const { data, info } = await (0, image_processor_setup_1.sharpQueue)(() => (0, sharp_1.default)(options.binaryFile)
|
|
35
|
+
.resize(options.imageSize)
|
|
36
|
+
.toFormat("png")
|
|
37
|
+
.toBuffer({ resolveWithObject: true }));
|
|
38
|
+
const binaryFile = icoEndec.encode([data]);
|
|
39
|
+
return {
|
|
40
|
+
width: info.width,
|
|
41
|
+
height: info.height,
|
|
42
|
+
weightB: info.size,
|
|
43
|
+
mediaType: "image/x-icon",
|
|
44
|
+
binaryFile,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
exports.resizeIcoImage = resizeIcoImage;
|
|
48
|
+
function convertMediaTypeToSharpFormat(mediaType) {
|
|
49
|
+
if (!mediaType.startsWith("image/") || !constants_1.authorizedMediaTypes.has(mediaType)) {
|
|
50
|
+
throw new Error(`invalid media-type '${mediaType}'`);
|
|
51
|
+
}
|
|
52
|
+
return mediaType.substring(6);
|
|
53
|
+
}
|
|
54
|
+
function convertSharpFormatToMediaType(format) {
|
|
55
|
+
return `image/${format}`;
|
|
56
|
+
}
|
|
57
|
+
exports.convertSharpFormatToMediaType = convertSharpFormatToMediaType;
|
|
58
|
+
function getQuality({ width, height }, qualityPolicy) {
|
|
59
|
+
const area = width * height;
|
|
60
|
+
for (const { quality, areaLimitPx } of qualityPolicy ?? []) {
|
|
61
|
+
if (areaLimitPx === undefined || area <= areaLimitPx)
|
|
62
|
+
return quality;
|
|
63
|
+
}
|
|
64
|
+
return 70;
|
|
65
|
+
}
|
|
66
|
+
exports.getQuality = getQuality;
|
|
67
|
+
//# sourceMappingURL=resizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resizer.js","sourceRoot":"","sources":["../../src/internal/resizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.processAllPending = void 0;
|
|
4
|
+
const queries_1 = require("../database/queries");
|
|
5
|
+
const resizer_1 = require("./resizer");
|
|
6
|
+
async function processAllPending(context) {
|
|
7
|
+
while (true) {
|
|
8
|
+
const task = context.queue.shift();
|
|
9
|
+
if (!task)
|
|
10
|
+
break;
|
|
11
|
+
const pth = context.pendingTasks.get(task.taskKey);
|
|
12
|
+
if (!pth) {
|
|
13
|
+
context.appLog.error(`Missing pending variant '${task.taskKey}'`);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const result = await excecuteTask(task, context);
|
|
18
|
+
pth.resolve(result);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
context.appLog.error(`[image-cache-engine] image '${task.imageUid}', '${task.variantName}':`, error, task);
|
|
22
|
+
pth.reject(error);
|
|
23
|
+
}
|
|
24
|
+
context.pendingTasks.delete(task.taskKey);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.processAllPending = processAllPending;
|
|
28
|
+
async function excecuteTask(task, ctx) {
|
|
29
|
+
const found = await (0, queries_1.fetchImageVariantWithBinary)(ctx, {
|
|
30
|
+
imageUid: task.imageUid,
|
|
31
|
+
variantName: task.variantName,
|
|
32
|
+
});
|
|
33
|
+
if (found) {
|
|
34
|
+
await (0, queries_1.markOneCacheKeyAsUsed)(ctx, {
|
|
35
|
+
imageCacheId: found.id,
|
|
36
|
+
cacheKey: task.dependsOnCacheKey,
|
|
37
|
+
});
|
|
38
|
+
return found.variant;
|
|
39
|
+
}
|
|
40
|
+
const origBinaryFile = await ctx.getOriginalImage(task.imageUid);
|
|
41
|
+
const result = await (0, resizer_1.resizeImage)({
|
|
42
|
+
binaryFile: origBinaryFile,
|
|
43
|
+
imageSize: task.imageSize,
|
|
44
|
+
formatTo: task.changeMediaTypeTo
|
|
45
|
+
? {
|
|
46
|
+
mediaType: task.changeMediaTypeTo,
|
|
47
|
+
}
|
|
48
|
+
: undefined,
|
|
49
|
+
qualityPolicy: ctx.qualityPolicy,
|
|
50
|
+
});
|
|
51
|
+
const { lastModified } = await (0, queries_1.insertIntoImageVariant)(ctx.cn, {
|
|
52
|
+
imageUid: task.imageUid,
|
|
53
|
+
mediaType: result.mediaType,
|
|
54
|
+
variantName: task.variantName,
|
|
55
|
+
width: result.width,
|
|
56
|
+
height: result.height,
|
|
57
|
+
weightB: result.weightB,
|
|
58
|
+
binaryFile: result.binaryFile,
|
|
59
|
+
}, task.dependsOnCacheKey);
|
|
60
|
+
return { ...result, lastModified };
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=task-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-processor.js","sourceRoot":"","sources":["../../src/internal/task-processor.ts"],"names":[],"mappings":";;;AAAA,iDAI6B;AAI7B,uCAAwC;AAEjC,KAAK,UAAU,iBAAiB,CAAC,OAA6B;IACnE,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnC,IAAI,CAAC,IAAI;YAAE,MAAM;QAEjB,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEnD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;YAClE,SAAS;QACX,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAEjD,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,+BAA+B,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC,WAAW,IAAI,EACvE,KAAK,EACL,IAAI,CACL,CAAC;YACF,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;QAED,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AA3BD,8CA2BC;AAED,KAAK,UAAU,YAAY,CACzB,IAAc,EACd,GAAyB;IAEzB,MAAM,KAAK,GAAG,MAAM,IAAA,qCAA2B,EAAC,GAAG,EAAE;QACnD,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,CAAC,CAAC;IACH,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,IAAA,+BAAqB,EAAC,GAAG,EAAE;YAC/B,YAAY,EAAE,KAAK,CAAC,EAAE;YACtB,QAAQ,EAAE,IAAI,CAAC,iBAAiB;SACjC,CAAC,CAAC;QACH,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEjE,MAAM,MAAM,GAAG,MAAM,IAAA,qBAAW,EAAC;QAC/B,UAAU,EAAE,cAAc;QAC1B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,QAAQ,EAAE,IAAI,CAAC,iBAAiB;YAC9B,CAAC,CAAC;gBACE,SAAS,EAAE,IAAI,CAAC,iBAAiB;aAClC;YACH,CAAC,CAAC,SAAS;QACb,aAAa,EAAE,GAAG,CAAC,aAAa;KACjC,CAAC,CAAC;IAEH,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,IAAA,gCAAsB,EACnD,GAAG,CAAC,EAAE,EACN;QACE,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,UAAU,EAAE,MAAM,CAAC,UAAU;KAC9B,EACD,IAAI,CAAC,iBAAiB,CACvB,CAAC;IAEF,OAAO,EAAE,GAAG,MAAM,EAAE,YAAY,EAAE,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { ImageCompressionQualityPolicy, ImagePolicy } from "@paroicms/anywhere-lib";
|
|
3
|
+
export interface BadRequestErrorType {
|
|
4
|
+
new (message?: string): Error;
|
|
5
|
+
}
|
|
6
|
+
export interface OriginalImage {
|
|
7
|
+
binaryFile: Buffer;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
weightB: number;
|
|
11
|
+
mediaType: string;
|
|
12
|
+
lossless: boolean | undefined;
|
|
13
|
+
}
|
|
14
|
+
export declare function processOriginalImage({ binaryFile, mediaType, BadRequestError, policy, defaultPolicy, qualityPolicy, }: {
|
|
15
|
+
binaryFile: Buffer;
|
|
16
|
+
mediaType: string;
|
|
17
|
+
BadRequestError: BadRequestErrorType;
|
|
18
|
+
policy?: ImagePolicy;
|
|
19
|
+
defaultPolicy: ImagePolicy;
|
|
20
|
+
qualityPolicy: ImageCompressionQualityPolicy[];
|
|
21
|
+
}): Promise<OriginalImage>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.processOriginalImage = void 0;
|
|
7
|
+
const sharp_1 = __importDefault(require("sharp"));
|
|
8
|
+
const constants_1 = require("./constants");
|
|
9
|
+
const image_processor_setup_1 = require("./image-processor-setup");
|
|
10
|
+
const resizer_1 = require("./internal/resizer");
|
|
11
|
+
async function processOriginalImage({ binaryFile, mediaType, BadRequestError, policy, defaultPolicy, qualityPolicy, }) {
|
|
12
|
+
const weightB = binaryFile.byteLength;
|
|
13
|
+
const sharpInst = (0, sharp_1.default)(binaryFile);
|
|
14
|
+
const { width, height, format } = await (0, image_processor_setup_1.sharpQueue)(() => sharpInst.metadata());
|
|
15
|
+
if (width === undefined || height === undefined || !format) {
|
|
16
|
+
throw new Error("cannot get image metadata");
|
|
17
|
+
}
|
|
18
|
+
const foundMediaType = (0, resizer_1.convertSharpFormatToMediaType)(format);
|
|
19
|
+
if (foundMediaType !== mediaType) {
|
|
20
|
+
throw new BadRequestError(`mismatch media type '${mediaType}' with '${foundMediaType}'`);
|
|
21
|
+
}
|
|
22
|
+
if (!constants_1.authorizedMediaTypes.has(mediaType)) {
|
|
23
|
+
throw new BadRequestError("Invalid image type");
|
|
24
|
+
}
|
|
25
|
+
const weightLimitB = policy?.weightLimitB ?? defaultPolicy.weightLimitB;
|
|
26
|
+
const areaLimitPx = policy?.areaLimitPx ?? defaultPolicy.areaLimitPx;
|
|
27
|
+
if (mediaType === "image/gif" || mediaType === "image/svg+xml") {
|
|
28
|
+
if (weightLimitB && weightB > weightLimitB) {
|
|
29
|
+
throw new BadRequestError("Image weight exceeds limit");
|
|
30
|
+
}
|
|
31
|
+
return { binaryFile, width, height, mediaType, weightB, lossless: undefined };
|
|
32
|
+
}
|
|
33
|
+
const input = {
|
|
34
|
+
width,
|
|
35
|
+
height,
|
|
36
|
+
mediaType,
|
|
37
|
+
sharpInst,
|
|
38
|
+
};
|
|
39
|
+
if (mediaType === "image/webp") {
|
|
40
|
+
if (weightLimitB === undefined || weightB <= weightLimitB) {
|
|
41
|
+
return { binaryFile, width, height, mediaType, weightB, lossless: undefined };
|
|
42
|
+
}
|
|
43
|
+
return await convertToWebp({ input, qualityPolicy, areaLimitPx, lossless: false });
|
|
44
|
+
}
|
|
45
|
+
if (mediaType === "image/png") {
|
|
46
|
+
return await convertToWebp({
|
|
47
|
+
input,
|
|
48
|
+
qualityPolicy,
|
|
49
|
+
areaLimitPx,
|
|
50
|
+
// WebP lossless images are 26% smaller in size compared to PNGs
|
|
51
|
+
// See: https://developers.google.com/speed/webp
|
|
52
|
+
lossless: weightLimitB === undefined || weightB <= weightLimitB * 1.35,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return await convertToWebp({ input, qualityPolicy, areaLimitPx, lossless: false });
|
|
56
|
+
}
|
|
57
|
+
exports.processOriginalImage = processOriginalImage;
|
|
58
|
+
async function convertToWebp({ input, qualityPolicy, areaLimitPx, lossless, }) {
|
|
59
|
+
const { width, height } = areaLimitPx === undefined ? input : computeOriginalSize(input, areaLimitPx);
|
|
60
|
+
const size = { width, height }; // to be sure we keep only these properties
|
|
61
|
+
const quality = (0, resizer_1.getQuality)(size, qualityPolicy);
|
|
62
|
+
const { data, info } = await (0, image_processor_setup_1.sharpQueue)(() => input.sharpInst
|
|
63
|
+
.resize(size)
|
|
64
|
+
.webp({
|
|
65
|
+
lossless,
|
|
66
|
+
quality,
|
|
67
|
+
})
|
|
68
|
+
.toBuffer({ resolveWithObject: true }));
|
|
69
|
+
// we keep it here for the moment
|
|
70
|
+
// console.log(
|
|
71
|
+
// `....convertToWebp: ${input.width}x${input.height} => ${info.width}x${info.height}, ${
|
|
72
|
+
// info.size / 1000
|
|
73
|
+
// }KB`,
|
|
74
|
+
// );
|
|
75
|
+
return {
|
|
76
|
+
width: info.width,
|
|
77
|
+
height: info.height,
|
|
78
|
+
weightB: info.size,
|
|
79
|
+
mediaType: "image/webp",
|
|
80
|
+
lossless,
|
|
81
|
+
binaryFile: data,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function computeOriginalSize(input, areaLimitPx) {
|
|
85
|
+
const inputArea = input.width * input.height;
|
|
86
|
+
if (inputArea <= areaLimitPx)
|
|
87
|
+
return input;
|
|
88
|
+
const ratio = input.width / input.height;
|
|
89
|
+
const width = Math.trunc(Math.sqrt(areaLimitPx * ratio));
|
|
90
|
+
const height = Math.round((input.height / input.width) * width);
|
|
91
|
+
return { width, height };
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=original-images-processing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"original-images-processing.js","sourceRoot":"","sources":["../src/original-images-processing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isVariantName = void 0;
|
|
4
|
+
function isVariantName(variantName) {
|
|
5
|
+
return /^([0-9]+x|x[0-9]+|x[0-9]+x|[0-9]+x[0-9]+)$/.test(variantName);
|
|
6
|
+
}
|
|
7
|
+
exports.isVariantName = isVariantName;
|
|
8
|
+
//# sourceMappingURL=public-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-helpers.js","sourceRoot":"","sources":["../src/public-helpers.ts"],"names":[],"mappings":";;;AAEA,SAAgB,aAAa,CAAC,WAAmB;IAC/C,OAAO,4CAA4C,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AACxE,CAAC;AAFD,sCAEC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@paroicms/server-image-cache-engine",
|
|
3
|
+
"version": "0.25.0",
|
|
4
|
+
"description": "The image variant engine that we use at Paroi.",
|
|
5
|
+
"author": "Paroi Team",
|
|
6
|
+
"main": "dist/api.js",
|
|
7
|
+
"keywords": [],
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"typings": "dist/api.d.ts",
|
|
10
|
+
"type": "commonjs",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"clear": "rimraf dist/*",
|
|
14
|
+
"dev": "tsc --watch --preserveWatchOutput",
|
|
15
|
+
"eslint": "eslint ."
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "~20.10.5",
|
|
19
|
+
"rimraf": "~5.0.5",
|
|
20
|
+
"typescript": "~5.3.3"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@paroi/async-lib": "~0.1.2",
|
|
24
|
+
"@paroi/data-formatters-lib": "~0.1.2",
|
|
25
|
+
"@paroicms/server-lib": "0.14.0",
|
|
26
|
+
"@paroicms/anywhere-lib": "0.53.0",
|
|
27
|
+
"ico-endec": "~0.1.6",
|
|
28
|
+
"sharp": "~0.33.1"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"typeorm": "0.3"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"ddl",
|
|
35
|
+
"dist"
|
|
36
|
+
]
|
|
37
|
+
}
|