@pro-laico/payload-images 0.1.0 → 0.2.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.
@@ -1 +1 @@
1
- {"version":3,"file":"generatedImages.d.ts","sourceRoot":"","sources":["../../src/collections/generatedImages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,SAAS,CAAA;AAK/D,eAAO,MAAM,qBAAqB,qBAAqB,CAAA;AAEvD,MAAM,WAAW,4BAA4B;IAC3C,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,+BAA+B,GAAI,OAAM,4BAAiC,KAAG,gBA6BzF,CAAA;AAED,+CAA+C;AAC/C,eAAO,MAAM,eAAe,EAAE,gBAAoD,CAAA"}
1
+ {"version":3,"file":"generatedImages.d.ts","sourceRoot":"","sources":["../../src/collections/generatedImages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,SAAS,CAAA;AAK/D,eAAO,MAAM,qBAAqB,qBAAqB,CAAA;AAEvD,MAAM,WAAW,4BAA4B;IAC3C,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,wFAAwF;IACxF,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,+BAA+B,GAAI,OAAM,4BAAiC,KAAG,gBAgCzF,CAAA;AAED,+CAA+C;AAC/C,eAAO,MAAM,eAAe,EAAE,gBAAoD,CAAA"}
@@ -25,6 +25,11 @@ export const GENERATED_IMAGES_SLUG = 'generated-images';
25
25
  read: authd,
26
26
  update: authd
27
27
  },
28
+ // Derived, disposable variants written on cache miss — opt out of @pro-laico/payload-revalidate's
29
+ // auto-attached hooks so every variant create doesn't fire a pointless bust event.
30
+ custom: {
31
+ revalidate: false
32
+ },
28
33
  admin: {
29
34
  hidden: true,
30
35
  group: 'Assets',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/collections/generatedImages.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug } from 'payload'\n\nimport { authd } from '../access'\nimport { IMAGE_MIME_TYPES } from '../transform/params'\n\nexport const GENERATED_IMAGES_SLUG = 'generated-images'\n\nexport interface CreateGeneratedImagesOptions {\n /** Slug for this collection. Default `generated-images`. */\n slug?: string\n /** Slug of the source image collection the variants point back to. Default `images`. */\n sourceSlug?: string\n}\n\n/**\n * The hidden, durable cache of on-demand image variants. The transform endpoint\n * writes one upload doc here per (source, settings, focal) combination; the source\n * `images` collection surfaces them through a `join` field and purges them when the\n * source changes or is deleted.\n *\n * It is an UPLOAD collection so variant bytes flow through whatever storage adapter\n * is configured (local disk, S3, Vercel Blob, …) — keeping the feature\n * platform-agnostic. With a cloud storage adapter, register it with a SERVER-upload\n * storage instance, since the endpoint creates docs server-side via the Local API.\n *\n * Deliberately carries NO revalidation hooks: variants are derived and disposable,\n * so busting cache tags on every cache-miss create would be pure churn.\n */\nexport const createGeneratedImagesCollection = (opts: CreateGeneratedImagesOptions = {}): CollectionConfig => {\n const slug = opts.slug || GENERATED_IMAGES_SLUG\n const sourceSlug = (opts.sourceSlug || 'images') as CollectionSlug\n\n return {\n slug,\n access: { create: authd, delete: authd, read: authd, update: authd },\n admin: { hidden: true, group: 'Assets', useAsTitle: 'cacheKey', defaultColumns: ['cacheKey', 'width', 'height', 'format'] },\n fields: [\n { name: 'source', type: 'relationship', relationTo: sourceSlug, required: true, index: true },\n { name: 'cacheKey', type: 'text', required: true, unique: true },\n {\n type: 'row',\n fields: [\n { name: 'fit', type: 'text' },\n { name: 'format', type: 'text' },\n { name: 'quality', type: 'number' },\n ],\n },\n {\n type: 'row',\n fields: [\n { name: 'focalX', type: 'number' },\n { name: 'focalY', type: 'number' },\n ],\n },\n ],\n upload: { mimeTypes: IMAGE_MIME_TYPES },\n }\n}\n\n/** The default generated-images collection. */\nexport const GeneratedImages: CollectionConfig = createGeneratedImagesCollection()\n"],"names":["authd","IMAGE_MIME_TYPES","GENERATED_IMAGES_SLUG","createGeneratedImagesCollection","opts","slug","sourceSlug","access","create","delete","read","update","admin","hidden","group","useAsTitle","defaultColumns","fields","name","type","relationTo","required","index","unique","upload","mimeTypes","GeneratedImages"],"mappings":"AAEA,SAASA,KAAK,QAAQ,eAAW;AACjC,SAASC,gBAAgB,QAAQ,yBAAqB;AAEtD,OAAO,MAAMC,wBAAwB,mBAAkB;AASvD;;;;;;;;;;;;;CAaC,GACD,OAAO,MAAMC,kCAAkC,CAACC,OAAqC,CAAC,CAAC;IACrF,MAAMC,OAAOD,KAAKC,IAAI,IAAIH;IAC1B,MAAMI,aAAcF,KAAKE,UAAU,IAAI;IAEvC,OAAO;QACLD;QACAE,QAAQ;YAAEC,QAAQR;YAAOS,QAAQT;YAAOU,MAAMV;YAAOW,QAAQX;QAAM;QACnEY,OAAO;YAAEC,QAAQ;YAAMC,OAAO;YAAUC,YAAY;YAAYC,gBAAgB;gBAAC;gBAAY;gBAAS;gBAAU;aAAS;QAAC;QAC1HC,QAAQ;YACN;gBAAEC,MAAM;gBAAUC,MAAM;gBAAgBC,YAAYd;gBAAYe,UAAU;gBAAMC,OAAO;YAAK;YAC5F;gBAAEJ,MAAM;gBAAYC,MAAM;gBAAQE,UAAU;gBAAME,QAAQ;YAAK;YAC/D;gBACEJ,MAAM;gBACNF,QAAQ;oBACN;wBAAEC,MAAM;wBAAOC,MAAM;oBAAO;oBAC5B;wBAAED,MAAM;wBAAUC,MAAM;oBAAO;oBAC/B;wBAAED,MAAM;wBAAWC,MAAM;oBAAS;iBACnC;YACH;YACA;gBACEA,MAAM;gBACNF,QAAQ;oBACN;wBAAEC,MAAM;wBAAUC,MAAM;oBAAS;oBACjC;wBAAED,MAAM;wBAAUC,MAAM;oBAAS;iBAClC;YACH;SACD;QACDK,QAAQ;YAAEC,WAAWxB;QAAiB;IACxC;AACF,EAAC;AAED,6CAA6C,GAC7C,OAAO,MAAMyB,kBAAoCvB,kCAAiC"}
1
+ {"version":3,"sources":["../../src/collections/generatedImages.ts"],"sourcesContent":["import type { CollectionConfig, CollectionSlug } from 'payload'\n\nimport { authd } from '../access'\nimport { IMAGE_MIME_TYPES } from '../transform/params'\n\nexport const GENERATED_IMAGES_SLUG = 'generated-images'\n\nexport interface CreateGeneratedImagesOptions {\n /** Slug for this collection. Default `generated-images`. */\n slug?: string\n /** Slug of the source image collection the variants point back to. Default `images`. */\n sourceSlug?: string\n}\n\n/**\n * The hidden, durable cache of on-demand image variants. The transform endpoint\n * writes one upload doc here per (source, settings, focal) combination; the source\n * `images` collection surfaces them through a `join` field and purges them when the\n * source changes or is deleted.\n *\n * It is an UPLOAD collection so variant bytes flow through whatever storage adapter\n * is configured (local disk, S3, Vercel Blob, …) — keeping the feature\n * platform-agnostic. With a cloud storage adapter, register it with a SERVER-upload\n * storage instance, since the endpoint creates docs server-side via the Local API.\n *\n * Deliberately carries NO revalidation hooks: variants are derived and disposable,\n * so busting cache tags on every cache-miss create would be pure churn.\n */\nexport const createGeneratedImagesCollection = (opts: CreateGeneratedImagesOptions = {}): CollectionConfig => {\n const slug = opts.slug || GENERATED_IMAGES_SLUG\n const sourceSlug = (opts.sourceSlug || 'images') as CollectionSlug\n\n return {\n slug,\n access: { create: authd, delete: authd, read: authd, update: authd },\n // Derived, disposable variants written on cache miss — opt out of @pro-laico/payload-revalidate's\n // auto-attached hooks so every variant create doesn't fire a pointless bust event.\n custom: { revalidate: false },\n admin: { hidden: true, group: 'Assets', useAsTitle: 'cacheKey', defaultColumns: ['cacheKey', 'width', 'height', 'format'] },\n fields: [\n { name: 'source', type: 'relationship', relationTo: sourceSlug, required: true, index: true },\n { name: 'cacheKey', type: 'text', required: true, unique: true },\n {\n type: 'row',\n fields: [\n { name: 'fit', type: 'text' },\n { name: 'format', type: 'text' },\n { name: 'quality', type: 'number' },\n ],\n },\n {\n type: 'row',\n fields: [\n { name: 'focalX', type: 'number' },\n { name: 'focalY', type: 'number' },\n ],\n },\n ],\n upload: { mimeTypes: IMAGE_MIME_TYPES },\n }\n}\n\n/** The default generated-images collection. */\nexport const GeneratedImages: CollectionConfig = createGeneratedImagesCollection()\n"],"names":["authd","IMAGE_MIME_TYPES","GENERATED_IMAGES_SLUG","createGeneratedImagesCollection","opts","slug","sourceSlug","access","create","delete","read","update","custom","revalidate","admin","hidden","group","useAsTitle","defaultColumns","fields","name","type","relationTo","required","index","unique","upload","mimeTypes","GeneratedImages"],"mappings":"AAEA,SAASA,KAAK,QAAQ,eAAW;AACjC,SAASC,gBAAgB,QAAQ,yBAAqB;AAEtD,OAAO,MAAMC,wBAAwB,mBAAkB;AASvD;;;;;;;;;;;;;CAaC,GACD,OAAO,MAAMC,kCAAkC,CAACC,OAAqC,CAAC,CAAC;IACrF,MAAMC,OAAOD,KAAKC,IAAI,IAAIH;IAC1B,MAAMI,aAAcF,KAAKE,UAAU,IAAI;IAEvC,OAAO;QACLD;QACAE,QAAQ;YAAEC,QAAQR;YAAOS,QAAQT;YAAOU,MAAMV;YAAOW,QAAQX;QAAM;QACnE,kGAAkG;QAClG,mFAAmF;QACnFY,QAAQ;YAAEC,YAAY;QAAM;QAC5BC,OAAO;YAAEC,QAAQ;YAAMC,OAAO;YAAUC,YAAY;YAAYC,gBAAgB;gBAAC;gBAAY;gBAAS;gBAAU;aAAS;QAAC;QAC1HC,QAAQ;YACN;gBAAEC,MAAM;gBAAUC,MAAM;gBAAgBC,YAAYhB;gBAAYiB,UAAU;gBAAMC,OAAO;YAAK;YAC5F;gBAAEJ,MAAM;gBAAYC,MAAM;gBAAQE,UAAU;gBAAME,QAAQ;YAAK;YAC/D;gBACEJ,MAAM;gBACNF,QAAQ;oBACN;wBAAEC,MAAM;wBAAOC,MAAM;oBAAO;oBAC5B;wBAAED,MAAM;wBAAUC,MAAM;oBAAO;oBAC/B;wBAAED,MAAM;wBAAWC,MAAM;oBAAS;iBACnC;YACH;YACA;gBACEA,MAAM;gBACNF,QAAQ;oBACN;wBAAEC,MAAM;wBAAUC,MAAM;oBAAS;oBACjC;wBAAED,MAAM;wBAAUC,MAAM;oBAAS;iBAClC;YACH;SACD;QACDK,QAAQ;YAAEC,WAAW1B;QAAiB;IACxC;AACF,EAAC;AAED,6CAA6C,GAC7C,OAAO,MAAM2B,kBAAoCzB,kCAAiC"}
@@ -1 +1 @@
1
- {"version":3,"file":"inlineLqip.d.ts","sourceRoot":"","sources":["../../src/components/inlineLqip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAc,KAAK,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,SAAS,CAAA;AAGxE,OAAO,EAA2B,KAAK,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAE7F,OAAO,KAAK,EAAE,GAAG,EAAgB,MAAM,qBAAqB,CAAA;AAS5D,MAAM,WAAW,cAAc;IAC7B,qGAAqG;IACrG,MAAM,EAAE,eAAe,CAAA;IACvB,mEAAmE;IACnE,MAAM,EAAE,gBAAgB,CAAA;IACxB,iEAAiE;IACjE,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,GAAG,CAAA;IACR,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,yGAAyG;IACzG,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,0HAA0H;AAC1H,eAAO,MAAM,kBAAkB,GAAU,4EAStC,cAAc,KAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAuB7C,CAAA"}
1
+ {"version":3,"file":"inlineLqip.d.ts","sourceRoot":"","sources":["../../src/components/inlineLqip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAc,KAAK,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,SAAS,CAAA;AAGxE,OAAO,EAA2B,KAAK,gBAAgB,EAAE,MAAM,8BAA8B,CAAA;AAE7F,OAAO,KAAK,EAAE,GAAG,EAAgB,MAAM,qBAAqB,CAAA;AAS5D,MAAM,WAAW,cAAc;IAC7B,qGAAqG;IACrG,MAAM,EAAE,eAAe,CAAA;IACvB,mEAAmE;IACnE,MAAM,EAAE,gBAAgB,CAAA;IACxB,iEAAiE;IACjE,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,GAAG,CAAA;IACR,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,yGAAyG;IACzG,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,0HAA0H;AAC1H,eAAO,MAAM,kBAAkB,GAAU,4EAStC,cAAc,KAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CA2B7C,CAAA"}
@@ -44,11 +44,22 @@ import { clampLqipQuality, resolveLqipWidth } from "../transform/lqip.js";
44
44
  base,
45
45
  maxInputPixels
46
46
  });
47
- if (!res.ok) return undefined;
47
+ if (!res.ok) {
48
+ warnLqipFailureOnce(`generation returned ${res.status} (${res.msg}) for source ${source.id}`);
49
+ return undefined;
50
+ }
48
51
  return `data:image/${ph.format};base64,${res.data.toString('base64')}`;
49
- } catch {
52
+ } catch (err) {
53
+ warnLqipFailureOnce(`threw for source ${source.id}: ${err instanceof Error ? err.message : String(err)}`);
50
54
  return undefined;
51
55
  }
52
56
  };
57
+ let warnedLqip = false;
58
+ /** A broken LQIP pipeline degrades to "no placeholder" — visually invisible — so say WHY once per
59
+ * process instead of swallowing it entirely. Callers still render fine without a placeholder. */ const warnLqipFailureOnce = (detail)=>{
60
+ if (warnedLqip) return;
61
+ warnedLqip = true;
62
+ console.warn(`[payload-images] inline LQIP ${detail} — placeholders are being skipped (this warns once per process).`);
63
+ };
53
64
 
54
65
  //# sourceMappingURL=inlineLqip.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/inlineLqip.ts"],"sourcesContent":["/**\n * Generate the inline LQIP for `<ResponsiveImage>`: a tiny, faithful (per aspect-ratio +\n * focal point) variant resolved to a base64 `data:` URI, server-side. Shares the one\n * `generated-images` cache via {@link getOrCreateVariantBytes}, so the LQIP for a given\n * (id, ar, focal) is built once ever — by whichever door (this, or the `/api/img` endpoint)\n * asks first.\n *\n * Server-only (pulls in `getPayload` + Sharp). `<ResponsiveImage>` imports it **dynamically**,\n * only when a placeholder is actually needed, so the lightweight render path never bundles it.\n */\nimport { getPayload, type Payload, type SanitizedConfig } from 'payload'\n\nimport { getServerSideURL } from '../lib/getServerSideURL'\nimport { getOrCreateVariantBytes, type VariantSourceDoc } from '../transform/getVariantBytes'\nimport { clampLqipQuality, resolveLqipWidth } from '../transform/lqip'\nimport type { Fit, ParsedParams } from '../transform/params'\n\ninterface PayloadImagesStash {\n sourceSlug?: string\n variantSlug?: string\n maxInputPixels?: number\n placeholder?: false | { width: number; quality: number; format: 'webp' | 'jpeg'; maxWidth: number }\n}\n\nexport interface InlineLqipArgs {\n /** The resolved Payload config (already in hand from `<ResponsiveImage>` / `req.payload.config`). */\n config: SanitizedConfig\n /** Populated source doc — needs `filename`/`url` + focal point. */\n source: VariantSourceDoc\n /** Render aspect ratio; omitted = the source's natural ratio. */\n ar?: number\n fit: Fit\n /** Per-read width override (else the project default). Clamped per `untrusted`. */\n width?: number\n /** Per-read quality override (else the project default). Clamped to 20–70. */\n quality?: number\n /**\n * Untrusted caller (the external `context.lqip` / `X-LQIP` door): clamp width to `maxWidth` and\n * snap to /8. The trusted component path leaves this false and its width is honored up to a guard.\n */\n untrusted?: boolean\n /** Reuse an existing Payload instance (e.g. `req.payload` in a hook); otherwise `getPayload(config)`. */\n payload?: Payload\n}\n\n/** Returns a `data:` URI for the LQIP, or `undefined` when disabled / not generatable (caller renders no placeholder). */\nexport const generateInlineLqip = async ({\n config,\n source,\n ar,\n fit,\n width,\n quality,\n untrusted = false,\n payload: existing,\n}: InlineLqipArgs): Promise<string | undefined> => {\n try {\n const stash = (config as { custom?: { payloadImages?: PayloadImagesStash } }).custom?.payloadImages\n const ph = stash?.placeholder\n if (!ph) return undefined // disabled project-wide\n\n const payload = existing ?? (await getPayload({ config }))\n const sourceSlug = stash?.sourceSlug ?? 'images'\n const variantSlug = stash?.variantSlug ?? 'generated-images'\n const maxInputPixels = stash?.maxInputPixels ?? 100_000_000\n const base = config.serverURL || getServerSideURL() || ''\n\n const w = resolveLqipWidth(width, ph.width, ph.maxWidth, untrusted)\n const q = clampLqipQuality(quality, ph.quality)\n const h = ar ? Math.max(1, Math.round(w / ar)) : undefined\n const params: ParsedParams = { w, h, fit, q, fmt: ph.format }\n\n const res = await getOrCreateVariantBytes({ payload, source, params, format: ph.format, sourceSlug, variantSlug, base, maxInputPixels })\n if (!res.ok) return undefined\n return `data:image/${ph.format};base64,${res.data.toString('base64')}`\n } catch {\n return undefined\n }\n}\n"],"names":["getPayload","getServerSideURL","getOrCreateVariantBytes","clampLqipQuality","resolveLqipWidth","generateInlineLqip","config","source","ar","fit","width","quality","untrusted","payload","existing","stash","custom","payloadImages","ph","placeholder","undefined","sourceSlug","variantSlug","maxInputPixels","base","serverURL","w","maxWidth","q","h","Math","max","round","params","fmt","format","res","ok","data","toString"],"mappings":"AAAA;;;;;;;;;CASC,GACD,SAASA,UAAU,QAA4C,UAAS;AAExE,SAASC,gBAAgB,QAAQ,6BAAyB;AAC1D,SAASC,uBAAuB,QAA+B,kCAA8B;AAC7F,SAASC,gBAAgB,EAAEC,gBAAgB,QAAQ,uBAAmB;AA+BtE,wHAAwH,GACxH,OAAO,MAAMC,qBAAqB,OAAO,EACvCC,MAAM,EACNC,MAAM,EACNC,EAAE,EACFC,GAAG,EACHC,KAAK,EACLC,OAAO,EACPC,YAAY,KAAK,EACjBC,SAASC,QAAQ,EACF;IACf,IAAI;QACF,MAAMC,QAAQ,AAACT,OAA+DU,MAAM,EAAEC;QACtF,MAAMC,KAAKH,OAAOI;QAClB,IAAI,CAACD,IAAI,OAAOE,UAAU,wBAAwB;;QAElD,MAAMP,UAAUC,YAAa,MAAMd,WAAW;YAAEM;QAAO;QACvD,MAAMe,aAAaN,OAAOM,cAAc;QACxC,MAAMC,cAAcP,OAAOO,eAAe;QAC1C,MAAMC,iBAAiBR,OAAOQ,kBAAkB;QAChD,MAAMC,OAAOlB,OAAOmB,SAAS,IAAIxB,sBAAsB;QAEvD,MAAMyB,IAAItB,iBAAiBM,OAAOQ,GAAGR,KAAK,EAAEQ,GAAGS,QAAQ,EAAEf;QACzD,MAAMgB,IAAIzB,iBAAiBQ,SAASO,GAAGP,OAAO;QAC9C,MAAMkB,IAAIrB,KAAKsB,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAACN,IAAIlB,OAAOY;QACjD,MAAMa,SAAuB;YAAEP;YAAGG;YAAGpB;YAAKmB;YAAGM,KAAKhB,GAAGiB,MAAM;QAAC;QAE5D,MAAMC,MAAM,MAAMlC,wBAAwB;YAAEW;YAASN;YAAQ0B;YAAQE,QAAQjB,GAAGiB,MAAM;YAAEd;YAAYC;YAAaE;YAAMD;QAAe;QACtI,IAAI,CAACa,IAAIC,EAAE,EAAE,OAAOjB;QACpB,OAAO,CAAC,WAAW,EAAEF,GAAGiB,MAAM,CAAC,QAAQ,EAAEC,IAAIE,IAAI,CAACC,QAAQ,CAAC,WAAW;IACxE,EAAE,OAAM;QACN,OAAOnB;IACT;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/components/inlineLqip.ts"],"sourcesContent":["/**\n * Generate the inline LQIP for `<ResponsiveImage>`: a tiny, faithful (per aspect-ratio +\n * focal point) variant resolved to a base64 `data:` URI, server-side. Shares the one\n * `generated-images` cache via {@link getOrCreateVariantBytes}, so the LQIP for a given\n * (id, ar, focal) is built once ever — by whichever door (this, or the `/api/img` endpoint)\n * asks first.\n *\n * Server-only (pulls in `getPayload` + Sharp). `<ResponsiveImage>` imports it **dynamically**,\n * only when a placeholder is actually needed, so the lightweight render path never bundles it.\n */\nimport { getPayload, type Payload, type SanitizedConfig } from 'payload'\n\nimport { getServerSideURL } from '../lib/getServerSideURL'\nimport { getOrCreateVariantBytes, type VariantSourceDoc } from '../transform/getVariantBytes'\nimport { clampLqipQuality, resolveLqipWidth } from '../transform/lqip'\nimport type { Fit, ParsedParams } from '../transform/params'\n\ninterface PayloadImagesStash {\n sourceSlug?: string\n variantSlug?: string\n maxInputPixels?: number\n placeholder?: false | { width: number; quality: number; format: 'webp' | 'jpeg'; maxWidth: number }\n}\n\nexport interface InlineLqipArgs {\n /** The resolved Payload config (already in hand from `<ResponsiveImage>` / `req.payload.config`). */\n config: SanitizedConfig\n /** Populated source doc — needs `filename`/`url` + focal point. */\n source: VariantSourceDoc\n /** Render aspect ratio; omitted = the source's natural ratio. */\n ar?: number\n fit: Fit\n /** Per-read width override (else the project default). Clamped per `untrusted`. */\n width?: number\n /** Per-read quality override (else the project default). Clamped to 20–70. */\n quality?: number\n /**\n * Untrusted caller (the external `context.lqip` / `X-LQIP` door): clamp width to `maxWidth` and\n * snap to /8. The trusted component path leaves this false and its width is honored up to a guard.\n */\n untrusted?: boolean\n /** Reuse an existing Payload instance (e.g. `req.payload` in a hook); otherwise `getPayload(config)`. */\n payload?: Payload\n}\n\n/** Returns a `data:` URI for the LQIP, or `undefined` when disabled / not generatable (caller renders no placeholder). */\nexport const generateInlineLqip = async ({\n config,\n source,\n ar,\n fit,\n width,\n quality,\n untrusted = false,\n payload: existing,\n}: InlineLqipArgs): Promise<string | undefined> => {\n try {\n const stash = (config as { custom?: { payloadImages?: PayloadImagesStash } }).custom?.payloadImages\n const ph = stash?.placeholder\n if (!ph) return undefined // disabled project-wide\n\n const payload = existing ?? (await getPayload({ config }))\n const sourceSlug = stash?.sourceSlug ?? 'images'\n const variantSlug = stash?.variantSlug ?? 'generated-images'\n const maxInputPixels = stash?.maxInputPixels ?? 100_000_000\n const base = config.serverURL || getServerSideURL() || ''\n\n const w = resolveLqipWidth(width, ph.width, ph.maxWidth, untrusted)\n const q = clampLqipQuality(quality, ph.quality)\n const h = ar ? Math.max(1, Math.round(w / ar)) : undefined\n const params: ParsedParams = { w, h, fit, q, fmt: ph.format }\n\n const res = await getOrCreateVariantBytes({ payload, source, params, format: ph.format, sourceSlug, variantSlug, base, maxInputPixels })\n if (!res.ok) {\n warnLqipFailureOnce(`generation returned ${res.status} (${res.msg}) for source ${source.id}`)\n return undefined\n }\n return `data:image/${ph.format};base64,${res.data.toString('base64')}`\n } catch (err) {\n warnLqipFailureOnce(`threw for source ${source.id}: ${err instanceof Error ? err.message : String(err)}`)\n return undefined\n }\n}\n\nlet warnedLqip = false\n\n/** A broken LQIP pipeline degrades to \"no placeholder\" — visually invisible — so say WHY once per\n * process instead of swallowing it entirely. Callers still render fine without a placeholder. */\nconst warnLqipFailureOnce = (detail: string): void => {\n if (warnedLqip) return\n warnedLqip = true\n console.warn(`[payload-images] inline LQIP ${detail} — placeholders are being skipped (this warns once per process).`)\n}\n"],"names":["getPayload","getServerSideURL","getOrCreateVariantBytes","clampLqipQuality","resolveLqipWidth","generateInlineLqip","config","source","ar","fit","width","quality","untrusted","payload","existing","stash","custom","payloadImages","ph","placeholder","undefined","sourceSlug","variantSlug","maxInputPixels","base","serverURL","w","maxWidth","q","h","Math","max","round","params","fmt","format","res","ok","warnLqipFailureOnce","status","msg","id","data","toString","err","Error","message","String","warnedLqip","detail","console","warn"],"mappings":"AAAA;;;;;;;;;CASC,GACD,SAASA,UAAU,QAA4C,UAAS;AAExE,SAASC,gBAAgB,QAAQ,6BAAyB;AAC1D,SAASC,uBAAuB,QAA+B,kCAA8B;AAC7F,SAASC,gBAAgB,EAAEC,gBAAgB,QAAQ,uBAAmB;AA+BtE,wHAAwH,GACxH,OAAO,MAAMC,qBAAqB,OAAO,EACvCC,MAAM,EACNC,MAAM,EACNC,EAAE,EACFC,GAAG,EACHC,KAAK,EACLC,OAAO,EACPC,YAAY,KAAK,EACjBC,SAASC,QAAQ,EACF;IACf,IAAI;QACF,MAAMC,QAAQ,AAACT,OAA+DU,MAAM,EAAEC;QACtF,MAAMC,KAAKH,OAAOI;QAClB,IAAI,CAACD,IAAI,OAAOE,UAAU,wBAAwB;;QAElD,MAAMP,UAAUC,YAAa,MAAMd,WAAW;YAAEM;QAAO;QACvD,MAAMe,aAAaN,OAAOM,cAAc;QACxC,MAAMC,cAAcP,OAAOO,eAAe;QAC1C,MAAMC,iBAAiBR,OAAOQ,kBAAkB;QAChD,MAAMC,OAAOlB,OAAOmB,SAAS,IAAIxB,sBAAsB;QAEvD,MAAMyB,IAAItB,iBAAiBM,OAAOQ,GAAGR,KAAK,EAAEQ,GAAGS,QAAQ,EAAEf;QACzD,MAAMgB,IAAIzB,iBAAiBQ,SAASO,GAAGP,OAAO;QAC9C,MAAMkB,IAAIrB,KAAKsB,KAAKC,GAAG,CAAC,GAAGD,KAAKE,KAAK,CAACN,IAAIlB,OAAOY;QACjD,MAAMa,SAAuB;YAAEP;YAAGG;YAAGpB;YAAKmB;YAAGM,KAAKhB,GAAGiB,MAAM;QAAC;QAE5D,MAAMC,MAAM,MAAMlC,wBAAwB;YAAEW;YAASN;YAAQ0B;YAAQE,QAAQjB,GAAGiB,MAAM;YAAEd;YAAYC;YAAaE;YAAMD;QAAe;QACtI,IAAI,CAACa,IAAIC,EAAE,EAAE;YACXC,oBAAoB,CAAC,oBAAoB,EAAEF,IAAIG,MAAM,CAAC,EAAE,EAAEH,IAAII,GAAG,CAAC,aAAa,EAAEjC,OAAOkC,EAAE,EAAE;YAC5F,OAAOrB;QACT;QACA,OAAO,CAAC,WAAW,EAAEF,GAAGiB,MAAM,CAAC,QAAQ,EAAEC,IAAIM,IAAI,CAACC,QAAQ,CAAC,WAAW;IACxE,EAAE,OAAOC,KAAK;QACZN,oBAAoB,CAAC,iBAAiB,EAAE/B,OAAOkC,EAAE,CAAC,EAAE,EAAEG,eAAeC,QAAQD,IAAIE,OAAO,GAAGC,OAAOH,MAAM;QACxG,OAAOxB;IACT;AACF,EAAC;AAED,IAAI4B,aAAa;AAEjB;gGACgG,GAChG,MAAMV,sBAAsB,CAACW;IAC3B,IAAID,YAAY;IAChBA,aAAa;IACbE,QAAQC,IAAI,CAAC,CAAC,6BAA6B,EAAEF,OAAO,gEAAgE,CAAC;AACvH"}
@@ -1 +1 @@
1
- {"version":3,"file":"getVariantBytes.d.ts","sourceRoot":"","sources":["../../src/transform/getVariantBytes.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAkB,OAAO,EAAE,MAAM,SAAS,CAAA;AAGtD,OAAO,EAA+B,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5F,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,UAAU,CAAA;AAE1E,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAA;AAEtH,qDAAqD;AACrD,MAAM,MAAM,QAAQ,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhH,yGAAyG;AACzG,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEhF,wGAAwG;AACxG,MAAM,MAAM,YAAY,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9I,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,6DAA6D;IAC7D,MAAM,EAAE,gBAAgB,CAAA;IACxB,yCAAyC;IACzC,MAAM,EAAE,YAAY,CAAA;IACpB,6CAA6C;IAC7C,MAAM,EAAE,YAAY,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAA;IACtB,gGAAgG;IAChG,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AA2BD,yGAAyG;AACzG,eAAO,MAAM,kBAAkB,0GAA0G,CAAA;AAEzI;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAAU,MAAM,mBAAmB,KAAG,OAAO,CAAC,YAAY,CAoF7F,CAAA"}
1
+ {"version":3,"file":"getVariantBytes.d.ts","sourceRoot":"","sources":["../../src/transform/getVariantBytes.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAkB,OAAO,EAAE,MAAM,SAAS,CAAA;AAGtD,OAAO,EAA+B,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAA;AAE5F,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,UAAU,CAAA;AAE1E,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAA;AAEtH,qDAAqD;AACrD,MAAM,MAAM,QAAQ,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhH,yGAAyG;AACzG,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEhF,wGAAwG;AACxG,MAAM,MAAM,YAAY,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9I,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,6DAA6D;IAC7D,MAAM,EAAE,gBAAgB,CAAA;IACxB,yCAAyC;IACzC,MAAM,EAAE,YAAY,CAAA;IACpB,6CAA6C;IAC7C,MAAM,EAAE,YAAY,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAA;IACtB,gGAAgG;IAChG,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AA2BD,yGAAyG;AACzG,eAAO,MAAM,kBAAkB,0GAA0G,CAAA;AAKzI;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAAU,MAAM,mBAAmB,KAAG,OAAO,CAAC,YAAY,CA8F7F,CAAA"}
@@ -32,6 +32,7 @@ import { readBytes, resolveStaticDir } from "./source.js";
32
32
  return /sharp|libvips/i.test(s) && /cannot find module|module_not_found|could not load|native|binding/i.test(s);
33
33
  };
34
34
  /** The actionable fix for a Sharp load failure — shared by the boot probe and the request-time catch. */ export const SHARP_INSTALL_HINT = "install it (`pnpm add sharp`) and externalize it in next.config (`serverExternalPackages: ['sharp']`)";
35
+ /** Once-per-process latch for the cache-lookup warning below. */ let warnedCacheLookup = false;
35
36
  /**
36
37
  * Return the bytes for one variant: cache hit → stored copy; miss → Sharp once, persist after
37
38
  * the response (via Next's `after()`, falling back to fire-and-forget), then return the bytes.
@@ -70,7 +71,17 @@ import { readBytes, resolveStaticDir } from "./source.js";
70
71
  key
71
72
  };
72
73
  }
73
- } catch {}
74
+ } catch (err) {
75
+ // Fall through to regeneration, but not SILENTLY: a broken variant collection would otherwise
76
+ // masquerade as a perpetual cache miss — every request re-transforms, with zero signal.
77
+ if (!warnedCacheLookup) {
78
+ warnedCacheLookup = true;
79
+ payload.logger.warn({
80
+ msg: `[payload-images] variant cache lookup failed for '${variantSlug}' — falling back to regenerating on every request until this is fixed (warns once per process).`,
81
+ err
82
+ });
83
+ }
84
+ }
74
85
  const generate = async ()=>{
75
86
  const original = await readBytes(src, resolveStaticDir(payload, sourceSlug), base, {
76
87
  payload,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/transform/getVariantBytes.ts"],"sourcesContent":["/**\n * The shared \"give me the bytes for this variant\" engine: compute the cache key, return the\n * cached variant if one exists, otherwise generate it with Sharp once, persist it after the\n * response, and return the bytes. Extracted from the transform endpoint so the on-demand\n * `/api/img` route and `<ResponsiveImage>`'s inline LQIP run the EXACT same\n * read-or-generate-or-persist path against the one `generated-images` cache — a given\n * (source, size, fit, quality, format) variant is generated once, whichever door triggered it.\n */\nimport { after } from 'next/server'\n\nimport type { CollectionSlug, Payload } from 'payload'\n\nimport { variantCacheKey } from '../variants/key'\nimport { extForFormat, mimeForFormat, type OutputFormat, type ParsedParams } from './params'\nimport { transformImage, type TransformOutput } from './sharp'\nimport { readBytes, resolveStaticDir, type UploadDocLike } from './source'\n\n/** A resolved source doc: id + where-the-bytes-live + focal point. */\nexport type VariantSourceDoc = UploadDocLike & { id: string | number; focalX?: number | null; focalY?: number | null }\n\n/** Generation outcome (bytes or a typed failure). */\nexport type GenBytes = { ok: true; data: Buffer; mimeType: string } | { ok: false; status: number; msg: string }\n\n/** Optional generation coalescer (the endpoint passes its per-process single-flight; omit at render). */\ntype GenFlight = (key: string, fn: () => Promise<GenBytes>) => Promise<GenBytes>\n\n/** Result of {@link getOrCreateVariantBytes} — bytes + the cache key (for ETag), or a typed failure. */\nexport type VariantBytes = { ok: true; data: Buffer; mimeType: string; key: string } | { ok: false; status: number; msg: string; key: string }\n\nexport interface GetVariantBytesArgs {\n payload: Payload\n /** Resolved source doc (id + filename/url + focal point). */\n source: VariantSourceDoc\n /** Parsed + snapped transform params. */\n params: ParsedParams\n /** Concrete output format (never `auto`). */\n format: OutputFormat\n sourceSlug: string\n variantSlug: string\n /** Origin used to read originals/variants served from a relative/cloud URL. */\n base: string\n /** Decompression-bomb / memory guard passed to Sharp. */\n maxInputPixels: number\n /** Generation coalescer; the endpoint passes its single-flight, render-time callers omit it. */\n genFlight?: GenFlight\n}\n\n/** True for a unique-constraint violation on the variant create. Two requests racing the same\n * cache miss both persist; the loser is expected, not noise. The violation arrives in several\n * shapes — a raw driver error mentioning duplicate/unique, a wrapper (drizzle's \"Failed query:\n * insert …\") whose `cause` carries the code (e.g. SQLITE_CONSTRAINT_UNIQUE), or Payload's\n * ValidationError (\"The following field is invalid: cacheKey\") — so walk the cause chain and\n * the field errors. */\nconst isDuplicateKeyError = (err: unknown): boolean => {\n let e: unknown = err\n for (let depth = 0; depth < 4 && e; depth++) {\n const msg = e instanceof Error ? e.message : String(e)\n const code = (e as { code?: unknown })?.code\n if (/duplicate|unique/i.test(`${msg} ${typeof code === 'string' ? code : ''}`)) return true\n e = (e as { cause?: unknown })?.cause\n }\n const fieldErrors = (err as { data?: { errors?: Array<{ message?: string; path?: string }> } })?.data?.errors\n return Array.isArray(fieldErrors) && fieldErrors.some((f) => f.path === 'cacheKey' || /unique/i.test(f.message ?? ''))\n}\n\n/** True when Sharp itself failed to load (module missing or native binding broken) — the fix is\n * the install, not this image, so the generic \"transform failed\" line alone would mislead. */\nconst isSharpLoadError = (err: unknown): boolean => {\n const s = `${String(err)} ${String((err as { code?: unknown })?.code ?? '')}`\n return /sharp|libvips/i.test(s) && /cannot find module|module_not_found|could not load|native|binding/i.test(s)\n}\n\n/** The actionable fix for a Sharp load failure — shared by the boot probe and the request-time catch. */\nexport const SHARP_INSTALL_HINT = \"install it (`pnpm add sharp`) and externalize it in next.config (`serverExternalPackages: ['sharp']`)\"\n\n/**\n * Return the bytes for one variant: cache hit → stored copy; miss → Sharp once, persist after\n * the response (via Next's `after()`, falling back to fire-and-forget), then return the bytes.\n * Generation is coalesced by cache key when a `genFlight` is supplied.\n */\nexport const getOrCreateVariantBytes = async (args: GetVariantBytesArgs): Promise<VariantBytes> => {\n const { payload, source: src, params: p, format, sourceSlug, variantSlug, base, maxInputPixels, genFlight } = args\n const key = variantCacheKey({ id: src.id, filename: src.filename, focalX: src.focalX, focalY: src.focalY }, p, format)\n\n // Cache hit → the stored variant's bytes.\n try {\n const hit = await payload.find({\n collection: variantSlug as CollectionSlug,\n where: { cacheKey: { equals: key } },\n limit: 1,\n depth: 0,\n overrideAccess: true,\n })\n const variant = hit?.docs?.[0] as (UploadDocLike & { id: string | number }) | undefined\n if (variant) {\n const bytes = await readBytes(variant, resolveStaticDir(payload, variantSlug), base, { payload, slug: variantSlug })\n if (bytes) return { ok: true, data: bytes, mimeType: mimeForFormat(format), key }\n }\n } catch {}\n\n const generate = async (): Promise<GenBytes> => {\n const original = await readBytes(src, resolveStaticDir(payload, sourceSlug), base, { payload, slug: sourceSlug })\n if (!original) {\n // Only the relative-URL path needs an origin to resolve; surface the serverURL hint\n // just here, when a read has actually failed — not preemptively at boot.\n const relative = !!src.url && !/^https?:\\/\\//i.test(src.url)\n const hint = relative ? ' — relative-URL storage and the request origin did not resolve; set serverURL in buildConfig' : ''\n payload.logger.warn(`[payload-images] source ${src.id} unreadable (filename=${src.filename ?? 'none'}, url=${src.url ?? 'none'})${hint}`)\n return { ok: false, status: 502, msg: 'Source unavailable' }\n }\n\n let out: TransformOutput\n try {\n out = await transformImage(original, {\n w: p.w,\n h: p.h,\n fit: p.fit,\n quality: p.q,\n format,\n focalX: src.focalX,\n focalY: src.focalY,\n maxInputPixels,\n })\n } catch (err) {\n const hint = isSharpLoadError(err) ? ` — sharp failed to load; ${SHARP_INSTALL_HINT}` : ''\n payload.logger.error(`[payload-images] transform failed for ${src.id}: ${String(err)}${hint}`)\n return { ok: false, status: 500, msg: 'Transform failed' }\n }\n\n const persist = async (): Promise<void> => {\n try {\n await payload.create({\n collection: variantSlug as CollectionSlug,\n file: { data: out.data, mimetype: out.mimeType, name: `${key}.${extForFormat(format)}`, size: out.data.byteLength },\n data: {\n source: src.id as never,\n cacheKey: key,\n fit: p.fit,\n format,\n quality: p.q,\n focalX: src.focalX ?? null,\n focalY: src.focalY ?? null,\n },\n overwriteExistingFiles: true,\n overrideAccess: true,\n })\n } catch (err) {\n if (!isDuplicateKeyError(err))\n payload.logger.warn(`[payload-images] failed to persist variant ${key} for source ${src.id}: ${String(err)}`)\n }\n }\n try {\n after(persist)\n } catch {\n void persist()\n }\n\n return { ok: true, data: out.data, mimeType: out.mimeType }\n }\n\n const result = genFlight ? await genFlight(key, generate) : await generate()\n return result.ok\n ? { ok: true, data: result.data, mimeType: result.mimeType, key }\n : { ok: false, status: result.status, msg: result.msg, key }\n}\n"],"names":["after","variantCacheKey","extForFormat","mimeForFormat","transformImage","readBytes","resolveStaticDir","isDuplicateKeyError","err","e","depth","msg","Error","message","String","code","test","cause","fieldErrors","data","errors","Array","isArray","some","f","path","isSharpLoadError","s","SHARP_INSTALL_HINT","getOrCreateVariantBytes","args","payload","source","src","params","p","format","sourceSlug","variantSlug","base","maxInputPixels","genFlight","key","id","filename","focalX","focalY","hit","find","collection","where","cacheKey","equals","limit","overrideAccess","variant","docs","bytes","slug","ok","mimeType","generate","original","relative","url","hint","logger","warn","status","out","w","h","fit","quality","q","error","persist","create","file","mimetype","name","size","byteLength","overwriteExistingFiles","result"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/transform/getVariantBytes.ts"],"sourcesContent":["/**\n * The shared \"give me the bytes for this variant\" engine: compute the cache key, return the\n * cached variant if one exists, otherwise generate it with Sharp once, persist it after the\n * response, and return the bytes. Extracted from the transform endpoint so the on-demand\n * `/api/img` route and `<ResponsiveImage>`'s inline LQIP run the EXACT same\n * read-or-generate-or-persist path against the one `generated-images` cache — a given\n * (source, size, fit, quality, format) variant is generated once, whichever door triggered it.\n */\nimport { after } from 'next/server'\n\nimport type { CollectionSlug, Payload } from 'payload'\n\nimport { variantCacheKey } from '../variants/key'\nimport { extForFormat, mimeForFormat, type OutputFormat, type ParsedParams } from './params'\nimport { transformImage, type TransformOutput } from './sharp'\nimport { readBytes, resolveStaticDir, type UploadDocLike } from './source'\n\n/** A resolved source doc: id + where-the-bytes-live + focal point. */\nexport type VariantSourceDoc = UploadDocLike & { id: string | number; focalX?: number | null; focalY?: number | null }\n\n/** Generation outcome (bytes or a typed failure). */\nexport type GenBytes = { ok: true; data: Buffer; mimeType: string } | { ok: false; status: number; msg: string }\n\n/** Optional generation coalescer (the endpoint passes its per-process single-flight; omit at render). */\ntype GenFlight = (key: string, fn: () => Promise<GenBytes>) => Promise<GenBytes>\n\n/** Result of {@link getOrCreateVariantBytes} — bytes + the cache key (for ETag), or a typed failure. */\nexport type VariantBytes = { ok: true; data: Buffer; mimeType: string; key: string } | { ok: false; status: number; msg: string; key: string }\n\nexport interface GetVariantBytesArgs {\n payload: Payload\n /** Resolved source doc (id + filename/url + focal point). */\n source: VariantSourceDoc\n /** Parsed + snapped transform params. */\n params: ParsedParams\n /** Concrete output format (never `auto`). */\n format: OutputFormat\n sourceSlug: string\n variantSlug: string\n /** Origin used to read originals/variants served from a relative/cloud URL. */\n base: string\n /** Decompression-bomb / memory guard passed to Sharp. */\n maxInputPixels: number\n /** Generation coalescer; the endpoint passes its single-flight, render-time callers omit it. */\n genFlight?: GenFlight\n}\n\n/** True for a unique-constraint violation on the variant create. Two requests racing the same\n * cache miss both persist; the loser is expected, not noise. The violation arrives in several\n * shapes — a raw driver error mentioning duplicate/unique, a wrapper (drizzle's \"Failed query:\n * insert …\") whose `cause` carries the code (e.g. SQLITE_CONSTRAINT_UNIQUE), or Payload's\n * ValidationError (\"The following field is invalid: cacheKey\") — so walk the cause chain and\n * the field errors. */\nconst isDuplicateKeyError = (err: unknown): boolean => {\n let e: unknown = err\n for (let depth = 0; depth < 4 && e; depth++) {\n const msg = e instanceof Error ? e.message : String(e)\n const code = (e as { code?: unknown })?.code\n if (/duplicate|unique/i.test(`${msg} ${typeof code === 'string' ? code : ''}`)) return true\n e = (e as { cause?: unknown })?.cause\n }\n const fieldErrors = (err as { data?: { errors?: Array<{ message?: string; path?: string }> } })?.data?.errors\n return Array.isArray(fieldErrors) && fieldErrors.some((f) => f.path === 'cacheKey' || /unique/i.test(f.message ?? ''))\n}\n\n/** True when Sharp itself failed to load (module missing or native binding broken) — the fix is\n * the install, not this image, so the generic \"transform failed\" line alone would mislead. */\nconst isSharpLoadError = (err: unknown): boolean => {\n const s = `${String(err)} ${String((err as { code?: unknown })?.code ?? '')}`\n return /sharp|libvips/i.test(s) && /cannot find module|module_not_found|could not load|native|binding/i.test(s)\n}\n\n/** The actionable fix for a Sharp load failure — shared by the boot probe and the request-time catch. */\nexport const SHARP_INSTALL_HINT = \"install it (`pnpm add sharp`) and externalize it in next.config (`serverExternalPackages: ['sharp']`)\"\n\n/** Once-per-process latch for the cache-lookup warning below. */\nlet warnedCacheLookup = false\n\n/**\n * Return the bytes for one variant: cache hit → stored copy; miss → Sharp once, persist after\n * the response (via Next's `after()`, falling back to fire-and-forget), then return the bytes.\n * Generation is coalesced by cache key when a `genFlight` is supplied.\n */\nexport const getOrCreateVariantBytes = async (args: GetVariantBytesArgs): Promise<VariantBytes> => {\n const { payload, source: src, params: p, format, sourceSlug, variantSlug, base, maxInputPixels, genFlight } = args\n const key = variantCacheKey({ id: src.id, filename: src.filename, focalX: src.focalX, focalY: src.focalY }, p, format)\n\n // Cache hit → the stored variant's bytes.\n try {\n const hit = await payload.find({\n collection: variantSlug as CollectionSlug,\n where: { cacheKey: { equals: key } },\n limit: 1,\n depth: 0,\n overrideAccess: true,\n })\n const variant = hit?.docs?.[0] as (UploadDocLike & { id: string | number }) | undefined\n if (variant) {\n const bytes = await readBytes(variant, resolveStaticDir(payload, variantSlug), base, { payload, slug: variantSlug })\n if (bytes) return { ok: true, data: bytes, mimeType: mimeForFormat(format), key }\n }\n } catch (err) {\n // Fall through to regeneration, but not SILENTLY: a broken variant collection would otherwise\n // masquerade as a perpetual cache miss — every request re-transforms, with zero signal.\n if (!warnedCacheLookup) {\n warnedCacheLookup = true\n payload.logger.warn({\n msg: `[payload-images] variant cache lookup failed for '${variantSlug}' — falling back to regenerating on every request until this is fixed (warns once per process).`,\n err,\n })\n }\n }\n\n const generate = async (): Promise<GenBytes> => {\n const original = await readBytes(src, resolveStaticDir(payload, sourceSlug), base, { payload, slug: sourceSlug })\n if (!original) {\n // Only the relative-URL path needs an origin to resolve; surface the serverURL hint\n // just here, when a read has actually failed — not preemptively at boot.\n const relative = !!src.url && !/^https?:\\/\\//i.test(src.url)\n const hint = relative ? ' — relative-URL storage and the request origin did not resolve; set serverURL in buildConfig' : ''\n payload.logger.warn(`[payload-images] source ${src.id} unreadable (filename=${src.filename ?? 'none'}, url=${src.url ?? 'none'})${hint}`)\n return { ok: false, status: 502, msg: 'Source unavailable' }\n }\n\n let out: TransformOutput\n try {\n out = await transformImage(original, {\n w: p.w,\n h: p.h,\n fit: p.fit,\n quality: p.q,\n format,\n focalX: src.focalX,\n focalY: src.focalY,\n maxInputPixels,\n })\n } catch (err) {\n const hint = isSharpLoadError(err) ? ` — sharp failed to load; ${SHARP_INSTALL_HINT}` : ''\n payload.logger.error(`[payload-images] transform failed for ${src.id}: ${String(err)}${hint}`)\n return { ok: false, status: 500, msg: 'Transform failed' }\n }\n\n const persist = async (): Promise<void> => {\n try {\n await payload.create({\n collection: variantSlug as CollectionSlug,\n file: { data: out.data, mimetype: out.mimeType, name: `${key}.${extForFormat(format)}`, size: out.data.byteLength },\n data: {\n source: src.id as never,\n cacheKey: key,\n fit: p.fit,\n format,\n quality: p.q,\n focalX: src.focalX ?? null,\n focalY: src.focalY ?? null,\n },\n overwriteExistingFiles: true,\n overrideAccess: true,\n })\n } catch (err) {\n if (!isDuplicateKeyError(err))\n payload.logger.warn(`[payload-images] failed to persist variant ${key} for source ${src.id}: ${String(err)}`)\n }\n }\n try {\n after(persist)\n } catch {\n void persist()\n }\n\n return { ok: true, data: out.data, mimeType: out.mimeType }\n }\n\n const result = genFlight ? await genFlight(key, generate) : await generate()\n return result.ok\n ? { ok: true, data: result.data, mimeType: result.mimeType, key }\n : { ok: false, status: result.status, msg: result.msg, key }\n}\n"],"names":["after","variantCacheKey","extForFormat","mimeForFormat","transformImage","readBytes","resolveStaticDir","isDuplicateKeyError","err","e","depth","msg","Error","message","String","code","test","cause","fieldErrors","data","errors","Array","isArray","some","f","path","isSharpLoadError","s","SHARP_INSTALL_HINT","warnedCacheLookup","getOrCreateVariantBytes","args","payload","source","src","params","p","format","sourceSlug","variantSlug","base","maxInputPixels","genFlight","key","id","filename","focalX","focalY","hit","find","collection","where","cacheKey","equals","limit","overrideAccess","variant","docs","bytes","slug","ok","mimeType","logger","warn","generate","original","relative","url","hint","status","out","w","h","fit","quality","q","error","persist","create","file","mimetype","name","size","byteLength","overwriteExistingFiles","result"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pro-laico/payload-images",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Image optimization for Payload CMS: an Images upload collection that stores only the original, an on-demand Sharp transform endpoint (focal-aware crop, format negotiation, built-in LQIP placeholders), a durable variant cache with two-way purge, an admin focal-point picker, and a responsive <ResponsiveImage> component.",
5
5
  "license": "MIT",
6
6
  "repository": {