UNPKG

@paroicms/server-image-cache-engine

Version:

The image variant engine that we use at Paroi.

121 lines 4.69 kB
import { createVariantEngineContext } from "./internal/engine-context.js"; import { clearAll, deleteOrphanVariants, deleteVariants, deleteVariantsByDependencies, fetchImageVariantWithBinary, fetchImageVariantWithBinaryByOwner, fetchUsedRawSizesByMedia, } from "./internal/queries.js"; import { processAllPending } from "./internal/task-processor.js"; import { makeOwnerKey, makeTaskKey } from "./internal/utils.js"; import { createOwnerTransaction } from "./owner-transaction/owner-transaction.js"; let pendingSeq = 0; export async function createImageCacheEngine(options) { const { context, isCleared } = await createVariantEngineContext(options); let processing = false; const pendingTransactions = new Map(); function checkStatus() { if (context.status !== "ready") { throw new Error(`ImageCacheEngine is ${context.status}`); } } function startQueue() { if (processing) return; processing = true; void processAllPending(context) .catch((error) => { context.logger.error("Error when proccessing =>", error); }) .then(() => { processing = false; }); } function addPending(closeNow) { const pendingId = ++pendingSeq; pendingTransactions.set(pendingId, closeNow); return () => { pendingTransactions.delete(pendingId); }; } const imageCache = { async getImageVariantWithBinary({ mediaId, mediaType, rawWidth, rawHeight }) { checkStatus(); const taskKey = makeTaskKey({ mediaId, rawSizeName: `${rawWidth}x${rawHeight}`, mediaType }); const pth = context.pendingTasks.get(taskKey); if (pth) return pth.promise; const found = await fetchImageVariantWithBinary(context, { mediaId, mediaType, rawWidth, rawHeight, }); return found ? found.variant : undefined; }, async getImageVariantWithBinaryByOwner(options) { checkStatus(); const oneOwnerKey = makeOwnerKey(options); const pth = context.pendingTaskByOwnerKeys.get(oneOwnerKey); if (pth) return pth.promise; const found = await fetchImageVariantWithBinaryByOwner(context, options); return found ? found.variant : undefined; }, async makeImageAvailable({ ownerHandle, isHandleReusable, canBeRetrievedByOwner, ...input }) { checkStatus(); const transaction = createOwnerTransaction({ engine: { context: context, startQueue, checkStatus, addPending }, ownerHandle, isHandleReusable, canBeRetrievedByOwner, }); const [result] = await transaction.makeImageAvailable([input]); await transaction.flush(); await transaction.close(); return result; }, createOwnerTransaction(ownerHandle) { checkStatus(); return createOwnerTransaction({ engine: { context: context, startQueue, checkStatus, addPending }, ownerHandle, isHandleReusable: false, canBeRetrievedByOwner: false, }); }, async invalidate(options) { checkStatus(); if ("ownerHandles" in options) { await deleteVariantsByDependencies(context, options); } else { await deleteVariants(context, options); } }, async cleanUnusedValues() { checkStatus(); const count = await deleteOrphanVariants(context); if (count !== undefined && count > 0) { context.logger.debug(`${count} unused cache image were deleted`); } }, async clearCache() { checkStatus(); await clearAll(context); context.logger.debug("cache is cleared"); }, async getUsedRawSizesByMedia(mediaId) { checkStatus(); return await fetchUsedRawSizesByMedia(context, mediaId); }, async close() { if (context.status === "closed") return; context.status = "closed"; for (const closeNow of pendingTransactions.values()) { await closeNow(); } await context.cn.destroy(); }, }; return { imageCache, isCleared, }; } //# sourceMappingURL=image-cache-engine.js.map