@pixelfiddler/core 0.1.5 → 0.1.7
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/dist/index.cjs +84 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -40
- package/dist/index.d.ts +89 -40
- package/dist/index.js +84 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -10,31 +10,32 @@ var DEFAULT_DEVICE_BREAKPOINTS = [
|
|
|
10
10
|
1024,
|
|
11
11
|
1280,
|
|
12
12
|
1536,
|
|
13
|
-
1920
|
|
13
|
+
1920,
|
|
14
|
+
2048,
|
|
15
|
+
3840
|
|
14
16
|
];
|
|
15
17
|
var DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384];
|
|
16
18
|
var DEFAULT_MAX_DPR = 2;
|
|
17
|
-
function
|
|
19
|
+
function calculatesCandidateWidths(params) {
|
|
18
20
|
const {
|
|
19
21
|
allBreakpoints,
|
|
20
22
|
deviceBreakpoints,
|
|
21
23
|
explicitWidth,
|
|
22
|
-
sizesAttr
|
|
24
|
+
sizesAttr,
|
|
25
|
+
maxDpr
|
|
23
26
|
} = params;
|
|
24
|
-
|
|
25
|
-
const sortedDevice = [...new Set(deviceBreakpoints)].sort((a, b) => a - b);
|
|
26
|
-
if (!sortedAll.length || !sortedDevice.length) {
|
|
27
|
+
if (!allBreakpoints.length || !deviceBreakpoints.length) {
|
|
27
28
|
return { candidates: [], descriptor: "w" };
|
|
28
29
|
}
|
|
29
|
-
const minDevice =
|
|
30
|
-
const maxDevice =
|
|
30
|
+
const minDevice = deviceBreakpoints[0];
|
|
31
|
+
const maxDevice = deviceBreakpoints[deviceBreakpoints.length - 1];
|
|
31
32
|
const vwRatios = parseVW(sizesAttr);
|
|
32
33
|
const hasVW = vwRatios.length > 0;
|
|
33
|
-
if (
|
|
34
|
-
const DPRs = [1, 2, 3];
|
|
34
|
+
if (explicitWidth && !hasVW) {
|
|
35
|
+
const DPRs = [1, 2, 3, 4].filter((item) => item <= maxDpr);
|
|
35
36
|
const candidates = Array.from(
|
|
36
37
|
new Set(
|
|
37
|
-
DPRs.map((dpr) => nearest(
|
|
38
|
+
DPRs.map((dpr) => nearest(allBreakpoints, explicitWidth * dpr)).map(clampMax)
|
|
38
39
|
)
|
|
39
40
|
).filter((w) => w <= MAX_IMAGE_WIDTH);
|
|
40
41
|
return {
|
|
@@ -43,52 +44,56 @@ function computeCandidateWidths(params) {
|
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
if (hasVW) {
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
const minPx = minDevice * minRatio;
|
|
49
|
-
const maxPx = maxDevice * maxRatio * DEFAULT_MAX_DPR;
|
|
50
|
-
const candidates = sortedAll.filter(
|
|
51
|
-
(w) => w >= minPx && w <= clampMax(maxPx)
|
|
52
|
-
);
|
|
47
|
+
const lowestRatio = Math.min(...vwRatios) / 100;
|
|
48
|
+
const minRequiredPx = minDevice * lowestRatio;
|
|
53
49
|
return {
|
|
54
|
-
|
|
50
|
+
// Responsive layout only uses device breakpoints
|
|
51
|
+
candidates: deviceBreakpoints.filter((w) => w >= minRequiredPx && w <= MAX_IMAGE_WIDTH),
|
|
55
52
|
descriptor: "w"
|
|
56
53
|
};
|
|
57
54
|
}
|
|
58
|
-
const
|
|
55
|
+
const upperLimit = maxDevice * maxDpr;
|
|
59
56
|
return {
|
|
60
|
-
candidates:
|
|
57
|
+
candidates: allBreakpoints.filter((w) => w <= upperLimit),
|
|
61
58
|
descriptor: "w"
|
|
62
59
|
};
|
|
63
60
|
}
|
|
64
|
-
var createResponsiveAttributes = (
|
|
61
|
+
var createResponsiveAttributes = (imageSrc, baseUrl, options) => {
|
|
65
62
|
const {
|
|
66
63
|
sizes,
|
|
67
64
|
width,
|
|
68
65
|
deviceBreakpoints = [...DEFAULT_DEVICE_BREAKPOINTS],
|
|
69
66
|
imageBreakpoints = [...DEFAULT_IMAGE_BREAKPOINTS],
|
|
70
67
|
transformations = {},
|
|
71
|
-
mode = "short"
|
|
72
|
-
|
|
68
|
+
mode = "short",
|
|
69
|
+
maxDpr = DEFAULT_MAX_DPR
|
|
70
|
+
} = options || {};
|
|
73
71
|
const baseWidth = transformations.width ?? width;
|
|
74
72
|
const sortedDeviceBreakpoints = [...deviceBreakpoints].sort((a, b) => a - b);
|
|
75
73
|
const sortedImageBreakpoints = [...imageBreakpoints].sort((a, b) => a - b);
|
|
76
74
|
const allBreakpoints = [.../* @__PURE__ */ new Set([...sortedImageBreakpoints, ...sortedDeviceBreakpoints])].sort((a, b) => a - b);
|
|
77
|
-
const { candidates, descriptor } =
|
|
75
|
+
const { candidates, descriptor } = calculatesCandidateWidths({
|
|
78
76
|
allBreakpoints,
|
|
79
77
|
deviceBreakpoints: sortedDeviceBreakpoints,
|
|
80
78
|
...baseWidth !== void 0 && { explicitWidth: baseWidth },
|
|
81
|
-
...sizes !== void 0 && { sizesAttr: sizes }
|
|
79
|
+
...sizes !== void 0 && { sizesAttr: sizes },
|
|
80
|
+
maxDpr
|
|
82
81
|
});
|
|
83
82
|
if (candidates.length === 0) {
|
|
84
|
-
return { src:
|
|
83
|
+
return { src: getFinalUrl(imageSrc, baseUrl) };
|
|
85
84
|
}
|
|
86
|
-
const srcSetEntries = candidates.map((w, index) => buildSrcEntries(w, index,
|
|
85
|
+
const srcSetEntries = candidates.map((w, index) => buildSrcEntries(imageSrc, w, index, transformations, mode, descriptor, baseUrl));
|
|
87
86
|
const largestWidth = candidates[candidates.length - 1];
|
|
88
87
|
const src = buildTransformationUrl(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
{
|
|
89
|
+
src: imageSrc,
|
|
90
|
+
baseUrl,
|
|
91
|
+
transformations: {
|
|
92
|
+
...transformations,
|
|
93
|
+
width: largestWidth
|
|
94
|
+
},
|
|
95
|
+
mode
|
|
96
|
+
}
|
|
92
97
|
);
|
|
93
98
|
const endSizes = sizes ?? (descriptor === "w" ? "100vw" : void 0);
|
|
94
99
|
return {
|
|
@@ -98,11 +103,14 @@ var createResponsiveAttributes = (config, options = {}) => {
|
|
|
98
103
|
...width !== void 0 ? { width } : {}
|
|
99
104
|
};
|
|
100
105
|
};
|
|
101
|
-
function buildSrcEntries(width, index,
|
|
106
|
+
function buildSrcEntries(imageSrc, width, index, transformations, mode, descriptor, baseUrl) {
|
|
102
107
|
const url = buildTransformationUrl(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
{
|
|
109
|
+
src: imageSrc,
|
|
110
|
+
baseUrl,
|
|
111
|
+
transformations: { ...transformations, width },
|
|
112
|
+
mode
|
|
113
|
+
}
|
|
106
114
|
);
|
|
107
115
|
const desc = descriptor === "w" ? `${width}w` : `${index + 1}x`;
|
|
108
116
|
return `${url} ${desc}`;
|
|
@@ -121,9 +129,40 @@ var clampMax = (v) => Math.min(v, MAX_IMAGE_WIDTH);
|
|
|
121
129
|
var nearest = (sortedBreakpoints, target) => sortedBreakpoints.find((w) => w >= target) ?? sortedBreakpoints[sortedBreakpoints.length - 1];
|
|
122
130
|
var parseVW = (sizes) => {
|
|
123
131
|
if (!sizes) return [];
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
132
|
+
const foundSizes = sizes.match(/(\d+(?:\.\d+)?)vw/g) || [];
|
|
133
|
+
return foundSizes.map((size) => parseInt(size, 10));
|
|
134
|
+
};
|
|
135
|
+
var getBaseUrlWithoutQuery = (url) => {
|
|
136
|
+
if (!url) return "";
|
|
137
|
+
const queryIndex = url.indexOf("?");
|
|
138
|
+
return queryIndex === -1 ? url : url.slice(0, queryIndex);
|
|
139
|
+
};
|
|
140
|
+
var isAbsoluteUrl = (url) => {
|
|
141
|
+
if (!url) return false;
|
|
142
|
+
return url.startsWith("http://") || url.startsWith("https://");
|
|
143
|
+
};
|
|
144
|
+
function getFinalUrl(src, baseUrl) {
|
|
145
|
+
const cleanSrc = getBaseUrlWithoutQuery(src);
|
|
146
|
+
if (isAbsoluteUrl(cleanSrc)) {
|
|
147
|
+
return removeTrailingSlash(cleanSrc);
|
|
148
|
+
} else {
|
|
149
|
+
const base = removeTrailingSlash(getBaseUrlWithoutQuery(baseUrl));
|
|
150
|
+
const normalizedPath = normalizePath(cleanSrc);
|
|
151
|
+
return `${base}${normalizedPath}`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function normalizePath(src) {
|
|
155
|
+
return src && !src.startsWith("/") ? `/${src}` : src;
|
|
156
|
+
}
|
|
157
|
+
var parseNumberLikeParam = (width) => {
|
|
158
|
+
if (typeof width === "number") {
|
|
159
|
+
return width;
|
|
160
|
+
}
|
|
161
|
+
const parsed = Number(width);
|
|
162
|
+
if (!Number.isFinite(parsed)) {
|
|
163
|
+
throw new Error(`Invalid width value: ${width}`);
|
|
164
|
+
}
|
|
165
|
+
return parsed;
|
|
127
166
|
};
|
|
128
167
|
|
|
129
168
|
// src/config.ts
|
|
@@ -210,21 +249,18 @@ var buildTransformationQueryParams = (options, mode = "short") => {
|
|
|
210
249
|
params.map(([k, v]) => [k, v])
|
|
211
250
|
).toString();
|
|
212
251
|
};
|
|
213
|
-
var
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
var buildTransformationUrl = (config, options, mode = "short") => {
|
|
218
|
-
const baseUrl = removeTrailingSlash(getBaseUrlWithoutQuery(config.baseUrl));
|
|
219
|
-
if (options.original) {
|
|
220
|
-
return `${baseUrl}?original`;
|
|
252
|
+
var buildTransformationUrl = ({ src, transformations = {}, baseUrl, mode = "short" }) => {
|
|
253
|
+
const url = getFinalUrl(src, baseUrl);
|
|
254
|
+
if (transformations.original) {
|
|
255
|
+
return `${url}?original`;
|
|
221
256
|
}
|
|
222
|
-
const query = buildTransformationQueryParams(
|
|
223
|
-
return `${
|
|
257
|
+
const query = buildTransformationQueryParams(transformations, mode);
|
|
258
|
+
return `${url}?${query}`;
|
|
224
259
|
};
|
|
225
260
|
|
|
226
261
|
exports.buildTransformationQueryParams = buildTransformationQueryParams;
|
|
227
262
|
exports.buildTransformationUrl = buildTransformationUrl;
|
|
228
263
|
exports.createResponsiveAttributes = createResponsiveAttributes;
|
|
264
|
+
exports.parseNumberLikeParam = parseNumberLikeParam;
|
|
229
265
|
//# sourceMappingURL=index.cjs.map
|
|
230
266
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/src-set.ts","../src/utils.ts","../src/config.ts","../src/url-builder.ts"],"names":[],"mappings":";;;AAOO,IAAM,eAAA,GAAkB,IAAA;AAKxB,IAAM,0BAAA,GAA6B;AAAA,EACtC,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA,IAAA;AAAA,EACA;AACJ,CAAA;AAKO,IAAM,yBAAA,GAA4B,CAAC,EAAA,EAAI,EAAA,EAAI,IAAI,EAAA,EAAI,EAAA,EAAI,GAAA,EAAK,GAAA,EAAK,GAAG,CAAA;AA2C3E,IAAM,eAAA,GAAkB,CAAA;AAExB,SAAS,uBAAuB,MAAA,EAKoB;AAChD,EAAA,MAAM;AAAA,IACF,cAAA;AAAA,IACA,iBAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACJ,GAAI,MAAA;AAGJ,EAAA,MAAM,SAAA,GAAY,CAAC,GAAG,IAAI,GAAA,CAAI,cAAc,CAAC,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,IAAI,CAAC,CAAA;AACnE,EAAA,MAAM,YAAA,GAAe,CAAC,GAAG,IAAI,GAAA,CAAI,iBAAiB,CAAC,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,IAAI,CAAC,CAAA;AAEzE,EAAA,IAAI,CAAC,SAAA,CAAU,MAAA,IAAU,CAAC,aAAa,MAAA,EAAQ;AAC3C,IAAA,OAAO,EAAE,UAAA,EAAY,EAAC,EAAG,YAAY,GAAA,EAAI;AAAA,EAC7C;AAEA,EAAA,MAAM,SAAA,GAAY,aAAa,CAAC,CAAA;AAChC,EAAA,MAAM,SAAA,GAAY,YAAA,CAAa,YAAA,CAAa,MAAA,GAAS,CAAC,CAAA;AAEtD,EAAA,MAAM,QAAA,GAAW,QAAQ,SAAS,CAAA;AAClC,EAAA,MAAM,KAAA,GAAQ,SAAS,MAAA,GAAS,CAAA;AAIhC,EAAA,IAAI,OAAO,aAAA,KAAkB,QAAA,IAAY,CAAC,KAAA,EAAO;AAC7C,IAAA,MAAM,IAAA,GAAO,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAErB,IAAA,MAAM,aAAa,KAAA,CAAM,IAAA;AAAA,MACrB,IAAI,GAAA;AAAA,QACA,IAAA,CACK,GAAA,CAAI,CAAA,GAAA,KAAO,OAAA,CAAQ,SAAA,EAAW,gBAAgB,GAAG,CAAC,CAAA,CAClD,GAAA,CAAI,QAAQ;AAAA;AACrB,KACJ,CAAE,MAAA,CAAO,CAAA,CAAA,KAAK,CAAA,IAAK,eAAe,CAAA;AAElC,IAAA,OAAO;AAAA,MACH,UAAA;AAAA,MACA,UAAA,EAAY;AAAA,KAChB;AAAA,EACJ;AAGA,EAAA,IAAI,KAAA,EAAO;AACP,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,GAAG,QAAQ,CAAA;AACrC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,GAAG,QAAQ,CAAA;AAErC,IAAA,MAAM,QAAQ,SAAA,GAAY,QAAA;AAC1B,IAAA,MAAM,KAAA,GAAQ,YAAY,QAAA,GAAW,eAAA;AAErC,IAAA,MAAM,aAAa,SAAA,CAAU,MAAA;AAAA,MACzB,CAAA,CAAA,KAAK,CAAA,IAAK,KAAA,IAAS,CAAA,IAAK,SAAS,KAAK;AAAA,KAC1C;AAEA,IAAA,OAAO;AAAA,MACH,UAAA;AAAA,MACA,UAAA,EAAY;AAAA,KAChB;AAAA,EACJ;AAGA,EAAA,MAAM,YAAY,SAAA,GAAY,eAAA;AAE9B,EAAA,OAAO;AAAA,IACH,YAAY,SAAA,CAAU,MAAA,CAAO,OAAK,CAAA,IAAK,QAAA,CAAS,SAAS,CAAC,CAAA;AAAA,IAC1D,UAAA,EAAY;AAAA,GAChB;AACJ;AA0CO,IAAM,0BAAA,GAA6B,CACtC,MAAA,EACA,OAAA,GAAkC,EAAC,KACX;AACxB,EAAA,MAAM;AAAA,IACF,KAAA;AAAA,IACA,KAAA;AAAA,IACA,iBAAA,GAAoB,CAAC,GAAG,0BAA0B,CAAA;AAAA,IAClD,gBAAA,GAAmB,CAAC,GAAG,yBAAyB,CAAA;AAAA,IAChD,kBAAkB,EAAC;AAAA,IACnB,IAAA,GAAO;AAAA,GACX,GAAI,OAAA;AAGJ,EAAA,MAAM,SAAA,GAAY,gBAAgB,KAAA,IAAS,KAAA;AAE3C,EAAA,MAAM,uBAAA,GAA0B,CAAC,GAAG,iBAAiB,CAAA,CAAE,KAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AAC3E,EAAA,MAAM,sBAAA,GAAyB,CAAC,GAAG,gBAAgB,CAAA,CAAE,KAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AAEzE,EAAA,MAAM,iBAAiB,CAAC,uBAAO,GAAA,CAAI,CAAC,GAAG,sBAAA,EAAwB,GAAG,uBAAuB,CAAC,CAAC,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,IAAI,CAAC,CAAA;AAEjH,EAAA,MAAM,EAAE,UAAA,EAAY,UAAA,EAAW,GAAI,sBAAA,CAAuB;AAAA,IACtD,cAAA;AAAA,IACA,iBAAA,EAAmB,uBAAA;AAAA,IACnB,GAAI,SAAA,KAAc,MAAA,IAAa,EAAE,eAAe,SAAA,EAAU;AAAA,IAC1D,GAAI,KAAA,KAAU,MAAA,IAAa,EAAE,WAAW,KAAA;AAAM,GACjD,CAAA;AAED,EAAA,IAAI,UAAA,CAAW,WAAW,CAAA,EAAG;AAEzB,IAAA,OAAO,EAAE,GAAA,EAAK,MAAA,CAAO,OAAA,EAAQ;AAAA,EACjC;AAGA,EAAA,MAAM,aAAA,GAAgB,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA,EAAG,KAAA,KAAW,eAAA,CAAgB,CAAA,EAAG,KAAA,EAAO,MAAA,EAAQ,eAAA,EAAiB,IAAA,EAAM,UAAU,CAAC,CAAA;AAGxH,EAAA,MAAM,YAAA,GAAe,UAAA,CAAW,UAAA,CAAW,MAAA,GAAS,CAAC,CAAA;AACrD,EAAA,MAAM,GAAA,GAAM,sBAAA;AAAA,IACR,MAAA;AAAA,IACA,EAAE,GAAG,eAAA,EAAiB,KAAA,EAAO,YAAA,EAAa;AAAA,IAC1C;AAAA,GACJ;AAEA,EAAA,MAAM,QAAA,GAAW,KAAA,KAAU,UAAA,KAAe,GAAA,GAAM,OAAA,GAAU,MAAA,CAAA;AAE1D,EAAA,OAAO;AAAA,IACH,GAAA;AAAA,IACA,GAAI,QAAA,GAAW,EAAE,KAAA,EAAO,QAAA,KAAa,EAAC;AAAA,IACtC,MAAA,EAAQ,aAAA,CAAc,IAAA,CAAK,IAAI,CAAA;AAAA,IAC/B,GAAI,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,KAAU;AAAC,GAC3C;AACJ;AAEA,SAAS,gBAAgB,KAAA,EAAe,KAAA,EAAe,MAAA,EAA0B,eAAA,EAAwC,MAAgB,UAAA,EAA+B;AACpK,EAAA,MAAM,GAAA,GAAM,sBAAA;AAAA,IACR,MAAA;AAAA,IACA,EAAE,GAAG,eAAA,EAAiB,KAAA,EAAa;AAAA,IACnC;AAAA,GACJ;AAEA,EAAA,MAAM,IAAA,GAAO,eAAe,GAAA,GAAM,CAAA,EAAG,KAAK,CAAA,CAAA,CAAA,GAAM,CAAA,EAAG,QAAQ,CAAC,CAAA,CAAA,CAAA;AAE5D,EAAA,OAAO,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AACzB;;;ACrPO,IAAM,eAAe,CAAC,KAAA,KAAkB,KAAA,CAAM,OAAA,CAAQ,KAAK,EAAE,CAAA;AAG7D,IAAM,mBAAA,GAAsB,CAAC,OAAA,KAAoB;AACpD,EAAA,OAAO,OAAA,CAAQ,SAAS,GAAG,CAAA,GACrB,QAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GACnB,OAAA;AACV,CAAA;AAEO,IAAM,cAAA,GAAiB,CAI1B,GAAA,EACA,IAAA,KAEA,IAAA,CAAK,MAAA;AAAA,EACD,CAAC,GAAA,EAAK,GAAA,KAAS,OAAO,IAAA,GAAO,MAAA,GAAY,IAAI,GAAG,CAAA;AAAA,EAChD;AACJ,CAAA;AAGG,IAAM,WAAW,CAAC,CAAA,KAAc,IAAA,CAAK,GAAA,CAAI,GAAG,eAAe,CAAA;AAE3D,IAAM,OAAA,GAAU,CAAC,iBAAA,EAA4B,MAAA,KAChD,iBAAA,CAAkB,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,IAAK,MAAM,CAAA,IAAK,iBAAA,CAAkB,iBAAA,CAAkB,SAAS,CAAC,CAAA;AAEvF,IAAM,OAAA,GAAU,CAAC,KAAA,KAA6B;AACjD,EAAA,IAAI,CAAC,KAAA,EAAO,OAAO,EAAC;AACpB,EAAA,OAAO,CAAC,GAAG,KAAA,CAAM,QAAA,CAAS,oBAAoB,CAAC,CAAA,CAAE,GAAA;AAAA,IAC7C,CAAA,CAAA,KAAK,MAAA,CAAO,CAAA,CAAE,CAAC,CAAC,CAAA,GAAI;AAAA,GACxB;AACJ,CAAA;;;AC/BO,IAAM,MAAA,GAAS;AAAA,EAClB,EAAC,MAAM,CAAC,OAAO,GAAG,IAAA,EAAM,OAAA,EAAS,OAAO,GAAA,EAAG;AAAA,EAC3C,EAAC,MAAM,CAAC,QAAQ,GAAG,IAAA,EAAM,QAAA,EAAU,OAAO,GAAA,EAAG;AAAA,EAC7C,EAAC,MAAM,CAAC,KAAK,GAAG,IAAA,EAAM,KAAA,EAAO,OAAO,KAAA,EAAK;AAAA,EACzC,EAAC,MAAM,CAAC,MAAM,GAAG,IAAA,EAAM,MAAA,EAAQ,OAAO,IAAA,EAAI;AAAA,EAC1C;AAAA,IACI,IAAA,EAAM,CAAC,YAAY,CAAA;AAAA,IACnB,IAAA,EAAM,YAAA;AAAA,IACN,KAAA,EAAO,IAAA;AAAA,IACP,SAAA,EAAW,CAAC,CAAA,KAAM,OAAO,MAAM,QAAA,GAAW,YAAA,CAAa,CAAC,CAAA,GAAI;AAAA,GAChE;AAAA,EAEA,EAAC,MAAM,CAAC,QAAQ,GAAG,IAAA,EAAM,QAAA,EAAU,OAAO,GAAA,EAAG;AAAA,EAC7C,EAAC,MAAM,CAAC,SAAS,GAAG,IAAA,EAAM,SAAA,EAAW,OAAO,GAAA,EAAG;AAAA,EAC/C,EAAC,MAAM,CAAC,eAAe,GAAG,IAAA,EAAM,eAAA,EAAiB,OAAO,IAAA,EAAI;AAAA,EAE5D,EAAC,MAAM,CAAC,MAAM,GAAG,IAAA,EAAM,MAAA,EAAQ,OAAO,IAAA,EAAI;AAAA,EAC1C,EAAC,MAAM,CAAC,YAAY,GAAG,IAAA,EAAM,YAAA,EAAc,OAAO,IAAA,EAAI;AAAA,EACtD,EAAC,MAAM,CAAC,UAAU,GAAG,IAAA,EAAM,UAAA,EAAY,OAAO,IAAA,EAAI;AAAA,EAClD,EAAC,MAAM,CAAC,WAAW,GAAG,IAAA,EAAM,WAAA,EAAa,OAAO,IAAA,EAAI;AAAA,EACpD,EAAC,MAAM,CAAC,YAAY,GAAG,IAAA,EAAM,YAAA,EAAc,OAAO,IAAA,EAAI;AAAA,EACtD,EAAC,MAAM,CAAC,OAAO,GAAG,IAAA,EAAM,OAAA,EAAS,OAAO,IAAA,EAAI;AAAA,EAC5C,EAAC,MAAM,CAAC,SAAS,GAAG,IAAA,EAAM,SAAA,EAAW,OAAO,IAAA,EAAI;AAAA,EAChD,EAAC,MAAM,CAAC,OAAO,GAAG,IAAA,EAAM,OAAA,EAAS,OAAO,IAAA,EAAI;AAAA,EAC5C,EAAC,MAAM,CAAC,QAAQ,GAAG,IAAA,EAAM,QAAA,EAAU,OAAO,IAAA,EAAI;AAAA,EAE9C,EAAC,MAAM,CAAC,QAAA,EAAU,OAAO,CAAA,EAAG,IAAA,EAAM,cAAA,EAAgB,KAAA,EAAO,MAAA,EAAM;AAAA,EAC/D;AAAA,IACI,IAAA,EAAM,CAAC,QAAA,EAAU,OAAO,CAAA;AAAA,IACxB,IAAA,EAAM,cAAA;AAAA,IACN,KAAA,EAAO,MAAA;AAAA,IACP,SAAA,EAAW,CAAC,CAAA,KAAM,OAAO,MAAM,QAAA,GAAW,YAAA,CAAa,CAAC,CAAA,GAAI;AAAA,GAChE;AAAA,EACA,EAAC,MAAM,CAAC,QAAA,EAAU,QAAQ,CAAA,EAAG,IAAA,EAAM,eAAA,EAAiB,KAAA,EAAO,MAAA,EAAM;AAAA,EAEjE,EAAC,MAAM,CAAC,MAAA,EAAQ,MAAM,CAAA,EAAG,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,MAAA,EAAM;AAAA,EACzD,EAAC,MAAM,CAAC,MAAA,EAAQ,YAAY,CAAA,EAAG,IAAA,EAAM,iBAAA,EAAmB,KAAA,EAAO,MAAA,EAAM;AAAA,EACrE,EAAC,IAAA,EAAM,CAAC,MAAA,EAAQ,OAAA,EAAS,UAAA,EAAY,GAAG,CAAA,EAAG,IAAA,EAAM,QAAA,EAAU,KAAA,EAAO,MAAA,EAAM;AAAA,EACxE,EAAC,IAAA,EAAM,CAAC,MAAA,EAAQ,OAAA,EAAS,UAAA,EAAY,GAAG,CAAA,EAAG,IAAA,EAAM,QAAA,EAAU,KAAA,EAAO,MAAA,EAAM;AAAA,EACxE,EAAC,IAAA,EAAM,CAAC,MAAA,EAAQ,OAAA,EAAS,UAAU,CAAA,EAAG,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAI;AAAA,EAC/D,EAAC,MAAM,CAAC,MAAA,EAAQ,OAAO,CAAA,EAAG,IAAA,EAAM,YAAA,EAAc,KAAA,EAAO,MAAA,EAAM;AAAA,EAC3D,EAAC,MAAM,CAAC,MAAA,EAAQ,QAAQ,CAAA,EAAG,IAAA,EAAM,aAAA,EAAe,KAAA,EAAO,MAAA,EAAM;AAAA,EAC7D,EAAC,MAAM,CAAC,MAAA,EAAQ,aAAa,CAAA,EAAG,IAAA,EAAM,kBAAA,EAAoB,KAAA,EAAO,OAAA,EAAO;AAAA,EAExE,EAAC,MAAM,CAAC,MAAA,EAAQ,MAAM,CAAA,EAAG,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAI;AAAA,EAClD;AAAA,IACI,IAAA,EAAM,CAAC,MAAA,EAAQ,OAAO,CAAA;AAAA,IACtB,IAAA,EAAM,YAAA;AAAA,IACN,KAAA,EAAO,MAAA;AAAA,IACP,SAAA,EAAW,CAAC,CAAA,KAAM,OAAO,MAAM,QAAA,GAAW,YAAA,CAAa,CAAC,CAAA,GAAI;AAAA,GAChE;AAAA,EACA,EAAC,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,EAAG,IAAA,EAAM,cAAA,EAAgB,KAAA,EAAO,MAAA,EAAM;AAAA,EAC/D,EAAC,MAAM,CAAC,MAAA,EAAQ,MAAM,CAAA,EAAG,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,MAAA,EAAM;AAAA,EACzD,EAAC,MAAM,CAAC,MAAA,EAAQ,MAAM,CAAA,EAAG,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,MAAA,EAAM;AAAA,EACzD,EAAC,MAAM,CAAC,MAAA,EAAQ,UAAU,CAAA,EAAG,IAAA,EAAM,eAAA,EAAiB,KAAA,EAAO,OAAA,EAAO;AAAA,EAClE,EAAC,MAAM,CAAC,MAAA,EAAQ,UAAU,CAAA,EAAG,IAAA,EAAM,eAAA,EAAiB,KAAA,EAAO,MAAA,EAAM;AAAA,EACjE;AAAA,IACI,IAAA,EAAM,CAAC,MAAA,EAAQ,YAAY,CAAA;AAAA,IAC3B,IAAA,EAAM,iBAAA;AAAA,IACN,KAAA,EAAO,OAAA;AAAA,IACP,SAAA,EAAW,CAAC,CAAA,KAAM,OAAO,MAAM,QAAA,GAAW,YAAA,CAAa,CAAC,CAAA,GAAI;AAAA,GAChE;AAAA,EACA,EAAC,MAAM,CAAC,MAAA,EAAQ,mBAAmB,CAAA,EAAG,IAAA,EAAM,wBAAA,EAA0B,KAAA,EAAO,SAAA,EAAS;AAAA,EAEtF,EAAC,MAAM,CAAC,WAAA,EAAa,MAAM,CAAA,EAAG,IAAA,EAAM,WAAA,EAAa,KAAA,EAAO,IAAA,EAAI;AAAA,EAC5D,EAAC,MAAM,CAAC,WAAA,EAAa,SAAS,CAAA,EAAG,IAAA,EAAM,mBAAA,EAAqB,KAAA,EAAO,MAAA,EAAM;AAAA,EACzE,EAAC,MAAM,CAAC,WAAA,EAAa,MAAM,CAAA,EAAG,IAAA,EAAM,gBAAA,EAAkB,KAAA,EAAO,MAAA,EAAM;AAAA,EACnE,EAAC,MAAM,CAAC,WAAA,EAAa,OAAO,CAAA,EAAG,IAAA,EAAM,iBAAA,EAAmB,KAAA,EAAO,MAAA,EAAM;AAAA,EACrE,EAAC,MAAM,CAAC,WAAA,EAAa,QAAQ,CAAA,EAAG,IAAA,EAAM,kBAAA,EAAoB,KAAA,EAAO,MAAA,EAAM;AAAA,EACvE,EAAC,MAAM,CAAC,WAAA,EAAa,UAAU,CAAA,EAAG,IAAA,EAAM,oBAAA,EAAsB,KAAA,EAAO,MAAA;AACzE,CAAA;;;ACrCO,IAAM,8BAAA,GAAiC,CAC1C,OAAA,EACA,IAAA,GAAiB,OAAA,KACR;AACT,EAAA,MAAM,SAAkC,EAAC;AAEzC,EAAA,KAAA,MAAW,QAAQ,MAAA,EAAQ;AACvB,IAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,OAAA,EAAS,IAAA,CAAK,IAAI,CAAA;AAC/C,IAAA,IAAI,SAAS,IAAA,EAAM;AAEnB,IAAA,MAAM,OACF,IAAA,KAAS,OAAA,IAAW,KAAK,KAAA,GAAQ,IAAA,CAAK,QAAQ,IAAA,CAAK,IAAA;AAEvD,IAAA,IAAI,UAAA,GAAa,KAAA;AACjB,IAAA,IAAI,eAAe,IAAA,EAAM;AACrB,MAAA,UAAA,GAAa,IAAA,CAAK,UAAU,KAAK,CAAA;AAAA,IACrC;AAEA,IAAA,MAAA,CAAO,KAAK,CAAC,IAAA,EAAM,MAAA,CAAO,UAAU,CAAC,CAAC,CAAA;AAAA,EAC1C;AAGA,EAAA,OAAO,IAAI,eAAA;AAAA,IACP,MAAA,CAAO,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,CAAC,CAAA,EAAG,CAAC,CAAC;AAAA,IAC/B,QAAA,EAAS;AAEf;AA2CA,IAAM,sBAAA,GAAyB,CAAC,GAAA,KAAwB;AACpD,EAAA,MAAM,UAAA,GAAa,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAClC,EAAA,OAAO,eAAe,EAAA,GAAK,GAAA,GAAM,GAAA,CAAI,KAAA,CAAM,GAAG,UAAU,CAAA;AAC5D,CAAA;AAEO,IAAM,sBAAA,GAAyB,CAClC,MAAA,EACA,OAAA,EACA,OAAiB,OAAA,KAChB;AAED,EAAA,MAAM,OAAA,GAAU,mBAAA,CAAoB,sBAAA,CAAuB,MAAA,CAAO,OAAO,CAAC,CAAA;AAE1E,EAAA,IAAI,QAAQ,QAAA,EAAU;AAClB,IAAA,OAAO,GAAG,OAAO,CAAA,SAAA,CAAA;AAAA,EACrB;AAEA,EAAA,MAAM,KAAA,GAAQ,8BAAA,CAA+B,OAAA,EAAS,IAAI,CAAA;AAE1D,EAAA,OAAO,CAAA,EAAG,OAAO,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA;AAC9B","file":"index.cjs","sourcesContent":["import { buildTransformationUrl, NameMode } from './url-builder';\r\nimport { TransformationOptions, UrlBuilderConfig } from './types';\r\nimport { clampMax, nearest, parseVW } from './utils';\r\n\r\n/**\r\n * Maximum supported image width\r\n */\r\nexport const MAX_IMAGE_WIDTH = 4096;\r\n\r\n/**\r\n * Default device breakpoints for responsive srcset generation\r\n */\r\nexport const DEFAULT_DEVICE_BREAKPOINTS = [\r\n 320,\r\n 375,\r\n 640,\r\n 768,\r\n 1024,\r\n 1280,\r\n 1536,\r\n 1920\r\n] as const;\r\n\r\n/**\r\n * Default image breakpoints for smaller images (icons, thumbnails)\r\n */\r\nexport const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const;\r\n\r\n/**\r\n * Options for responsive image attributes generation\r\n */\r\nexport interface ResponsiveImageOptions {\r\n /**\r\n * The `sizes` attribute value (e.g., \"100vw\", \"(max-width: 768px) 100vw, 50vw\").\r\n * When provided, generates width descriptors (`w`) for responsive layouts.\r\n */\r\n sizes?: string;\r\n /**\r\n * Fixed display width in CSS pixels.\r\n * When provided without `sizes`, generates DPR descriptors (`x`).\r\n */\r\n width?: number;\r\n /** Device breakpoints for viewport-based sizing (default: DEFAULT_DEVICE_BREAKPOINTS) */\r\n deviceBreakpoints?: number[];\r\n /** Image breakpoints for smaller images (default: DEFAULT_IMAGE_BREAKPOINTS) */\r\n imageBreakpoints?: number[];\r\n /** Base transformation options to apply to all variants */\r\n transformations?: TransformationOptions;\r\n /** Whether to use short or long parameter names (default: 'short') */\r\n mode?: NameMode;\r\n}\r\n\r\n/**\r\n * Result of responsive image attributes generation\r\n */\r\nexport interface ResponsiveImageResult {\r\n /** The src attribute value (largest/default variant) */\r\n src: string;\r\n /** The srcset attribute value */\r\n srcSet?: string;\r\n /** The sizes attribute value (for width descriptors) */\r\n sizes?: string;\r\n /** The width attribute value (when explicitly provided) */\r\n width?: number;\r\n}\r\n\r\n/**\r\n * Computes candidate widths and descriptor type based on input options.\r\n */\r\nconst DEFAULT_MAX_DPR = 2;\r\n\r\nfunction computeCandidateWidths(params: {\r\n allBreakpoints: number[];\r\n deviceBreakpoints: number[];\r\n explicitWidth?: number;\r\n sizesAttr?: string;\r\n}): { candidates: number[]; descriptor: 'w' | 'x' } {\r\n const {\r\n allBreakpoints,\r\n deviceBreakpoints,\r\n explicitWidth,\r\n sizesAttr\r\n } = params;\r\n\r\n // ---- Normalize & guard ----\r\n const sortedAll = [...new Set(allBreakpoints)].sort((a, b) => a - b);\r\n const sortedDevice = [...new Set(deviceBreakpoints)].sort((a, b) => a - b);\r\n\r\n if (!sortedAll.length || !sortedDevice.length) {\r\n return { candidates: [], descriptor: 'w' };\r\n }\r\n\r\n const minDevice = sortedDevice[0]!;\r\n const maxDevice = sortedDevice[sortedDevice.length - 1]!;\r\n\r\n const vwRatios = parseVW(sizesAttr);\r\n const hasVW = vwRatios.length > 0;\r\n\r\n // Explicit width (fixed layout) → x descriptors ----\r\n // Prefer this when layout width is known and sizes does NOT imply vw scaling\r\n if (typeof explicitWidth === 'number' && !hasVW) {\r\n const DPRs = [1, 2, 3];\r\n\r\n const candidates = Array.from(\r\n new Set(\r\n DPRs\r\n .map(dpr => nearest(sortedAll, explicitWidth * dpr))\r\n .map(clampMax)\r\n )\r\n ).filter(w => w <= MAX_IMAGE_WIDTH);\r\n\r\n return {\r\n candidates,\r\n descriptor: 'x'\r\n };\r\n }\r\n\r\n // Sizes with vw → w descriptors ----\r\n if (hasVW) {\r\n const minRatio = Math.min(...vwRatios);\r\n const maxRatio = Math.max(...vwRatios);\r\n\r\n const minPx = minDevice * minRatio;\r\n const maxPx = maxDevice * maxRatio * DEFAULT_MAX_DPR;\r\n\r\n const candidates = sortedAll.filter(\r\n w => w >= minPx && w <= clampMax(maxPx)\r\n );\r\n\r\n return {\r\n candidates,\r\n descriptor: 'w'\r\n };\r\n }\r\n\r\n // No sizes & no explicit width → device-based w ----\r\n const maxUseful = maxDevice * DEFAULT_MAX_DPR;\r\n\r\n return {\r\n candidates: sortedAll.filter(w => w <= clampMax(maxUseful)),\r\n descriptor: 'w'\r\n };\r\n}\r\n\r\n/**\r\n * Generates responsive image attributes (src, srcSet, sizes) for optimal loading.\r\n *\r\n * Supports two strategies:\r\n * 1. **Responsive (with `sizes`)**: Uses width descriptors (`w`) for fluid layouts\r\n * where image size varies with viewport.\r\n * 2. **Fixed width (with `width`)**: Uses DPR descriptors (`x`) for fixed-size\r\n * images that need high-DPI support.\r\n *\r\n * @param config - Configuration containing the base URL for the image\r\n * @param options - Options for responsive image generation\r\n * @returns Object containing src, srcSet, and optionally sizes attributes\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/photo.jpg' };\r\n *\r\n * // Responsive image with sizes attribute (uses `w` descriptors)\r\n * createResponsiveAttributes(config, { sizes: '(max-width: 768px) 100vw, 50vw' });\r\n * // => {\r\n * // src: '...?w=3840',\r\n * // srcSet: '...?w=640 640w, ...?w=750 750w, ..., ...?w=3840 3840w',\r\n * // sizes: '(max-width: 768px) 100vw, 50vw'\r\n * // }\r\n *\r\n * // Fixed-width image (uses `x` descriptors)\r\n * createResponsiveAttributes(config, { width: 400 });\r\n * // => {\r\n * // src: '...?w=384',\r\n * // srcSet: '...?w=384 1x, ...?w=828 2x',\r\n * // width: 400\r\n * // }\r\n *\r\n * // With transformations\r\n * createResponsiveAttributes(config, {\r\n * sizes: '100vw',\r\n * transformations: { format: 'WEBP', quality: 80 }\r\n * });\r\n * ```\r\n */\r\nexport const createResponsiveAttributes = (\r\n config: UrlBuilderConfig,\r\n options: ResponsiveImageOptions = {}\r\n): ResponsiveImageResult => {\r\n const {\r\n sizes,\r\n width,\r\n deviceBreakpoints = [...DEFAULT_DEVICE_BREAKPOINTS],\r\n imageBreakpoints = [...DEFAULT_IMAGE_BREAKPOINTS],\r\n transformations = {},\r\n mode = 'short',\r\n } = options;\r\n\r\n // If transformations.width is set, it takes precedence as the base width\r\n const baseWidth = transformations.width ?? width;\r\n\r\n const sortedDeviceBreakpoints = [...deviceBreakpoints].sort((a, b) => a - b);\r\n const sortedImageBreakpoints = [...imageBreakpoints].sort((a, b) => a - b);\r\n\r\n const allBreakpoints = [...new Set([...sortedImageBreakpoints, ...sortedDeviceBreakpoints])].sort((a, b) => a - b);\r\n\r\n const { candidates, descriptor } = computeCandidateWidths({\r\n allBreakpoints,\r\n deviceBreakpoints: sortedDeviceBreakpoints,\r\n ...(baseWidth !== undefined && { explicitWidth: baseWidth }),\r\n ...(sizes !== undefined && { sizesAttr: sizes }),\r\n });\r\n\r\n if (candidates.length === 0) {\r\n // Fallback: no valid candidates, return base URL\r\n return { src: config.baseUrl };\r\n }\r\n\r\n // Build srcSet entries\r\n const srcSetEntries = candidates.map((w, index,) => buildSrcEntries(w, index, config, transformations, mode, descriptor));\r\n\r\n // Default src is the largest candidate\r\n const largestWidth = candidates[candidates.length - 1]!;\r\n const src = buildTransformationUrl(\r\n config,\r\n { ...transformations, width: largestWidth },\r\n mode\r\n );\r\n\r\n const endSizes = sizes ?? (descriptor === 'w' ? '100vw' : undefined);\r\n\r\n return {\r\n src,\r\n ...(endSizes ? { sizes: endSizes } : {}),\r\n srcSet: srcSetEntries.join(', '),\r\n ...(width !== undefined ? { width } : {}),\r\n };\r\n};\r\n\r\nfunction buildSrcEntries(width: number, index: number, config: UrlBuilderConfig, transformations: TransformationOptions, mode: NameMode, descriptor: 'w' | 'x'): string {\r\n const url = buildTransformationUrl(\r\n config,\r\n { ...transformations, width: width },\r\n mode\r\n );\r\n\r\n const desc = descriptor === 'w' ? `${width}w` : `${index + 1}x`;\r\n\r\n return `${url} ${desc}`;\r\n}\r\n","import { Path, PathValue } from './util-types';\r\nimport { MAX_IMAGE_WIDTH } from './src-set';\r\n\r\nexport const normalizeHex = (value: string) => value.replace('#', '')\r\n\r\n\r\nexport const removeTrailingSlash = (baseUrl: string) => {\r\n return baseUrl.endsWith('/')\r\n ? baseUrl.slice(0, -1)\r\n : baseUrl;\r\n}\r\n\r\nexport const getNestedValue = <\r\n T,\r\n P extends Path<T>\r\n>(\r\n obj: T,\r\n path: P\r\n): PathValue<T, P> | undefined =>\r\n path.reduce<any>(\r\n (acc, key) => (acc == null ? undefined : acc[key]),\r\n obj\r\n );\r\n\r\n\r\nexport const clampMax = (v: number) => Math.min(v, MAX_IMAGE_WIDTH);\r\n\r\nexport const nearest = (sortedBreakpoints: number[],target: number) =>\r\n sortedBreakpoints.find(w => w >= target) ?? sortedBreakpoints[sortedBreakpoints.length - 1]!;\r\n\r\nexport const parseVW = (sizes?: string): number[] => {\r\n if (!sizes) return [];\r\n return [...sizes.matchAll(/(\\d+(?:\\.\\d+)?)vw/g)].map(\r\n m => Number(m[1]) / 100\r\n );\r\n};\r\n\r\n","import { ParamSpec, Path } from './util-types';\r\nimport { TransformationOptions } from './types';\r\nimport { normalizeHex } from './utils';\r\n\r\nexport const PARAMS = [\r\n {path: ['width'], long: 'width', short: 'w'},\r\n {path: ['height'], long: 'height', short: 'h'},\r\n {path: ['dpr'], long: 'dpr', short: 'dpr'},\r\n {path: ['mode'], long: 'mode', short: 'rm'},\r\n {\r\n path: ['background'],\r\n long: 'background',\r\n short: 'bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n\r\n {path: ['format'], long: 'format', short: 'f'},\r\n {path: ['quality'], long: 'quality', short: 'q'},\r\n {path: ['stripMetadata'], long: 'stripMetadata', short: 'st'},\r\n\r\n {path: ['blur'], long: 'blur', short: 'bl'},\r\n {path: ['brightness'], long: 'brightness', short: 'br'},\r\n {path: ['contrast'], long: 'contrast', short: 'ct'},\r\n {path: ['grayscale'], long: 'grayscale', short: 'gs'},\r\n {path: ['saturation'], long: 'saturation', short: 'sa'},\r\n {path: ['sepia'], long: 'sepia', short: 'sp'},\r\n {path: ['sharpen'], long: 'sharpen', short: 'sh'},\r\n {path: ['noise'], long: 'noise', short: 'ns'},\r\n {path: ['rotate'], long: 'rotate', short: 'rt'},\r\n\r\n {path: ['border', 'width'], long: 'border.width', short: 'bo.w'},\r\n {\r\n path: ['border', 'color'],\r\n long: 'border.color',\r\n short: 'bo.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['border', 'radius'], long: 'border.radius', short: 'bo.r'},\r\n\r\n {path: ['crop', 'type'], long: 'crop.type', short: 'cr.t'},\r\n {path: ['crop', 'objectType'], long: 'crop.objectType', short: 'cr.o'},\r\n {path: ['crop', 'focus', 'position', 'x'], long: 'crop.x', short: 'cr.x'},\r\n {path: ['crop', 'focus', 'position', 'y'], long: 'crop.y', short: 'cr.y'},\r\n {path: ['crop', 'focus', 'strategy'], long: 'crop', short: 'cr'},\r\n {path: ['crop', 'width'], long: 'crop.width', short: 'cr.w'},\r\n {path: ['crop', 'height'], long: 'crop.height', short: 'cr.h'},\r\n {path: ['crop', 'afterResize'], long: 'crop.afterResize', short: 'cr.ar'},\r\n\r\n {path: ['text', 'text'], long: 'text', short: 'tx'},\r\n {\r\n path: ['text', 'color'],\r\n long: 'text.color',\r\n short: 'tx.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'opacity'], long: 'text.opacity', short: 'tx.o'},\r\n {path: ['text', 'size'], long: 'text.size', short: 'tx.s'},\r\n {path: ['text', 'font'], long: 'text.font', short: 'tx.f'},\r\n {path: ['text', 'fontSize'], long: 'text.fontSize', short: 'tx.fs'},\r\n {path: ['text', 'position'], long: 'text.position', short: 'tx.p'},\r\n {\r\n path: ['text', 'background'],\r\n long: 'text.background',\r\n short: 'tx.bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'backgroundOpacity'], long: 'text.backgroundOpacity', short: 'tx.bg.o'},\r\n\r\n {path: ['watermark', 'name'], long: 'watermark', short: 'wm'},\r\n {path: ['watermark', 'opacity'], long: 'watermark.opacity', short: 'wm.o'},\r\n {path: ['watermark', 'size'], long: 'watermark.size', short: 'wm.s'},\r\n {path: ['watermark', 'width'], long: 'watermark.width', short: 'wm.w'},\r\n {path: ['watermark', 'height'], long: 'watermark.height', short: 'wm.h'},\r\n {path: ['watermark', 'position'], long: 'watermark.position', short: 'wm.p'},\r\n] as const satisfies readonly ParamSpec<\r\n TransformationOptions,\r\n Path<TransformationOptions>\r\n>[];\r\n","import { TransformationOptions, UrlBuilderConfig } from './types';\r\nimport { getNestedValue, removeTrailingSlash } from './utils';\r\nimport { PARAMS } from './config';\r\n\r\n/**\r\n * Determines which parameter names to use in the query string.\r\n * - `'short'` - Use abbreviated aliases (e.g., `w`, `h`, `q`)\r\n * - `'long'` - Use full parameter names (e.g., `width`, `height`, `quality`)\r\n */\r\nexport type NameMode = 'short' | 'long';\r\n\r\n/**\r\n * Converts transformation options into a URL-encoded query string.\r\n *\r\n * Iterates through all defined parameters in the PARAMS config, extracts values\r\n * from the options object using their paths, applies any normalization functions\r\n * (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.\r\n *\r\n * @param options - The transformation options object\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns URL-encoded query string (without leading `?`)\r\n *\r\n * @example\r\n * ```ts\r\n * // Using short aliases (default)\r\n * buildQueryParams({ width: 800, height: 600, quality: 80 });\r\n * // => 'w=800&h=600&q=80'\r\n *\r\n * // Using long parameter names\r\n * buildQueryParams({ width: 800, format: 'webp' }, 'long');\r\n * // => 'width=800&format=webp'\r\n *\r\n * // Nested options\r\n * buildQueryParams({ border: { width: 2, color: '#ff0000' } });\r\n * // => 'bo.w=2&bo.c=ff0000'\r\n * ```\r\n */\r\nexport const buildTransformationQueryParams = (\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n): string => {\r\n const params: Array<[string, string]> = [];\r\n\r\n for (const spec of PARAMS) {\r\n const value = getNestedValue(options, spec.path);\r\n if (value == null) continue;\r\n\r\n const name =\r\n mode === 'short' && spec.short ? spec.short : spec.long;\r\n\r\n let normalized = value\r\n if ('normalize' in spec) {\r\n normalized = spec.normalize(value)\r\n }\r\n\r\n params.push([name, String(normalized)]);\r\n }\r\n\r\n\r\n return new URLSearchParams(\r\n params.map(([k, v]) => [k, v])\r\n ).toString();\r\n\r\n};\r\n\r\n/**\r\n * Builds a complete transformation URL from a base URL and transformation options.\r\n *\r\n * Combines the configured base URL with query parameters generated from the\r\n * transformation options. Handles the special `original` flag which returns\r\n * the unmodified source image.\r\n *\r\n * @param config - Configuration containing the base URL for the image\r\n * @param options - The transformation options to apply\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns The complete URL string with query parameters\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/images/photo.jpg' };\r\n *\r\n * // Basic resize\r\n * buildTransformationUrl(config, { width: 400, height: 300 });\r\n * // => 'https://example.com/images/photo.jpg?w=400&h=300'\r\n *\r\n * // Multiple transformations\r\n * buildTransformationUrl(config, {\r\n * width: 800,\r\n * format: 'webp',\r\n * quality: 85,\r\n * blur: 5\r\n * });\r\n * // => 'https://example.com/images/photo.jpg?w=800&f=webp&q=85&bl=5'\r\n *\r\n * // Request original image (no transformations)\r\n * buildTransformationUrl(config, { original: true });\r\n * // => 'https://example.com/images/photo.jpg?original'\r\n *\r\n * // Using long parameter names\r\n * buildTransformationUrl(config, { width: 400 }, 'long');\r\n * // => 'https://example.com/images/photo.jpg?width=400'\r\n * ```\r\n */\r\n/**\r\n * Extracts the base URL path without query parameters\r\n */\r\nconst getBaseUrlWithoutQuery = (url: string): string => {\r\n const queryIndex = url.indexOf('?');\r\n return queryIndex === -1 ? url : url.slice(0, queryIndex);\r\n};\r\n\r\nexport const buildTransformationUrl = (\r\n config: UrlBuilderConfig,\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n) => {\r\n // Strip any existing query params from baseUrl - transformations replace them\r\n const baseUrl = removeTrailingSlash(getBaseUrlWithoutQuery(config.baseUrl));\r\n\r\n if (options.original) {\r\n return `${baseUrl}?original`;\r\n }\r\n\r\n const query = buildTransformationQueryParams(options, mode);\r\n\r\n return `${baseUrl}?${query}`;\r\n};\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/src-set.ts","../src/utils.ts","../src/config.ts","../src/url-builder.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import { buildTransformationUrl, NameMode } from './url-builder';\r\nimport { TransformationOptions } from './types';\r\nimport { clampMax, getFinalUrl, nearest, parseVW } from './utils';\r\n\r\n/**\r\n * Maximum supported image width\r\n */\r\nexport const MAX_IMAGE_WIDTH = 4096;\r\n\r\n/**\r\n * Default device breakpoints for responsive srcset generation\r\n */\r\nexport const DEFAULT_DEVICE_BREAKPOINTS = [\r\n 320,\r\n 375,\r\n 640,\r\n 768,\r\n 1024,\r\n 1280,\r\n 1536,\r\n 1920,\r\n 2048,\r\n 3840\r\n] as const;\r\n\r\n/**\r\n * Default image breakpoints for smaller images (icons, thumbnails)\r\n */\r\nexport const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const;\r\n\r\n/**\r\n * Options for responsive image attributes generation\r\n */\r\nexport interface ResponsiveImageOptions {\r\n /**\r\n * The `sizes` attribute value (e.g., \"100vw\", \"(max-width: 768px) 100vw, 50vw\").\r\n * When provided, generates width descriptors (`w`) for responsive layouts.\r\n */\r\n sizes?: string;\r\n /**\r\n * Fixed display width in CSS pixels.\r\n * When provided without `sizes`, generates DPR descriptors (`x`).\r\n */\r\n width?: number;\r\n /** Device breakpoints for viewport-based sizing (default: DEFAULT_DEVICE_BREAKPOINTS) */\r\n deviceBreakpoints?: number[];\r\n /** Image breakpoints for smaller images (default: DEFAULT_IMAGE_BREAKPOINTS) */\r\n imageBreakpoints?: number[];\r\n /** Base transformation options to apply to all variants */\r\n transformations?: TransformationOptions;\r\n /** Whether to use short or long parameter names (default: 'short') */\r\n mode?: NameMode;\r\n maxDpr?: number\r\n}\r\n\r\n/**\r\n * Result of responsive image attributes generation\r\n */\r\nexport interface ResponsiveImageResult {\r\n /** The src attribute value (largest/default variant) */\r\n src: string;\r\n /** The srcset attribute value */\r\n srcSet?: string;\r\n /** The sizes attribute value (for width descriptors) */\r\n sizes?: string;\r\n /** The width attribute value (when explicitly provided) */\r\n width?: number;\r\n}\r\n\r\n/**\r\n * Calculates candidate widths and descriptor type based on input options.\r\n */\r\nconst DEFAULT_MAX_DPR = 2;\r\n\r\nfunction calculatesCandidateWidths(params: {\r\n allBreakpoints: number[];\r\n deviceBreakpoints: number[];\r\n explicitWidth?: number;\r\n sizesAttr?: string;\r\n maxDpr: number\r\n}): { candidates: number[]; descriptor: 'w' | 'x' } {\r\n const {\r\n allBreakpoints,\r\n deviceBreakpoints,\r\n explicitWidth,\r\n sizesAttr,\r\n maxDpr\r\n } = params;\r\n\r\n if (!allBreakpoints.length || !deviceBreakpoints.length) {\r\n return {candidates: [], descriptor: 'w'};\r\n }\r\n\r\n const minDevice = deviceBreakpoints[0]!;\r\n const maxDevice = deviceBreakpoints[deviceBreakpoints.length - 1]!;\r\n\r\n const vwRatios = parseVW(sizesAttr);\r\n const hasVW = vwRatios.length > 0;\r\n\r\n\r\n // When no sizes with vw definition is provided go for x descriptor\r\n if (explicitWidth && !hasVW) {\r\n const DPRs = [1, 2, 3, 4].filter(item => item <= maxDpr);\r\n\r\n const candidates = Array.from(\r\n new Set(\r\n DPRs\r\n .map(dpr => nearest(allBreakpoints, explicitWidth * dpr))\r\n .map(clampMax)\r\n )\r\n ).filter(w => w <= MAX_IMAGE_WIDTH);\r\n\r\n return {\r\n candidates,\r\n descriptor: 'x'\r\n };\r\n }\r\n\r\n\r\n // Sizes with vw → w descriptors ----\r\n if (hasVW) {\r\n const lowestRatio = Math.min(...vwRatios) / 100;\r\n const minRequiredPx = minDevice * lowestRatio;\r\n\r\n\r\n return {\r\n // Responsive layout only uses device breakpoints\r\n candidates: deviceBreakpoints.filter(w => w >= minRequiredPx && w <= MAX_IMAGE_WIDTH),\r\n descriptor: 'w',\r\n };\r\n }\r\n\r\n // No sizes & no explicit width → device-based w ----\r\n const upperLimit = maxDevice * maxDpr;\r\n\r\n return {\r\n candidates: allBreakpoints.filter(w => w <= upperLimit),\r\n descriptor: 'w'\r\n };\r\n}\r\n\r\n/**\r\n * Generates responsive image attributes (src, srcSet, sizes) for optimal loading.\r\n *\r\n * Supports two strategies:\r\n * 1. **Responsive (with `sizes`)**: Uses width descriptors (`w`) for fluid layouts\r\n * where image size varies with viewport.\r\n * 2. **Fixed width (with `width`)**: Uses DPR descriptors (`x`) for fixed-size\r\n * images that need high-DPI support.\r\n *\r\n * @param imageSrc - Relative or an absolute path to the image\r\n * @param baseUrl - This should be your baseURL endpoint for the source from PixelFiddle Dashboard.\r\n * @param options - Options for responsive image generation\r\n * @returns Object containing src, srcSet, and optionally sizes attributes\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/photo.jpg' };\r\n *\r\n * // Responsive image with sizes attribute (uses `w` descriptors)\r\n * createResponsiveAttributes(config, { sizes: '(max-width: 768px) 100vw, 50vw' });\r\n * // => {\r\n * // src: '...?w=3840',\r\n * // srcSet: '...?w=640 640w, ...?w=750 750w, ..., ...?w=3840 3840w',\r\n * // sizes: '(max-width: 768px) 100vw, 50vw'\r\n * // }\r\n *\r\n * // Fixed-width image (uses `x` descriptors)\r\n * createResponsiveAttributes(config, { width: 400 });\r\n * // => {\r\n * // src: '...?w=384',\r\n * // srcSet: '...?w=384 1x, ...?w=828 2x',\r\n * // width: 400\r\n * // }\r\n *\r\n * // With transformations\r\n * createResponsiveAttributes(config, {\r\n * sizes: '100vw',\r\n * transformations: { format: 'WEBP', quality: 80 }\r\n * });\r\n * ```\r\n */\r\nexport const createResponsiveAttributes = (\r\n imageSrc: string,\r\n baseUrl?: string,\r\n options?: ResponsiveImageOptions\r\n): ResponsiveImageResult => {\r\n const {\r\n sizes,\r\n width,\r\n deviceBreakpoints = [...DEFAULT_DEVICE_BREAKPOINTS],\r\n imageBreakpoints = [...DEFAULT_IMAGE_BREAKPOINTS],\r\n transformations = {},\r\n mode = 'short',\r\n maxDpr = DEFAULT_MAX_DPR\r\n } = options || {}\r\n\r\n // If transformations.width is set, it takes precedence as the base width\r\n const baseWidth = transformations.width ?? width;\r\n\r\n const sortedDeviceBreakpoints = [...deviceBreakpoints].sort((a, b) => a - b);\r\n const sortedImageBreakpoints = [...imageBreakpoints].sort((a, b) => a - b);\r\n\r\n const allBreakpoints = [...new Set([...sortedImageBreakpoints, ...sortedDeviceBreakpoints])].sort((a, b) => a - b);\r\n\r\n const {candidates, descriptor} = calculatesCandidateWidths({\r\n allBreakpoints,\r\n deviceBreakpoints: sortedDeviceBreakpoints,\r\n ...(baseWidth !== undefined && {explicitWidth: baseWidth}),\r\n ...(sizes !== undefined && {sizesAttr: sizes}),\r\n maxDpr\r\n });\r\n\r\n if (candidates.length === 0) {\r\n // Fallback: no valid candidates, return base URL\r\n return {src: getFinalUrl(imageSrc, baseUrl)};\r\n }\r\n\r\n // Build srcSet entries\r\n const srcSetEntries = candidates.map((w, index,) => buildSrcEntries(imageSrc, w, index, transformations, mode, descriptor, baseUrl));\r\n\r\n // Default src is the largest candidate\r\n const largestWidth = candidates[candidates.length - 1]!;\r\n const src = buildTransformationUrl({\r\n src: imageSrc,\r\n baseUrl,\r\n transformations: {\r\n ...transformations,\r\n width: largestWidth\r\n },\r\n mode\r\n }\r\n );\r\n\r\n const endSizes = sizes ?? (descriptor === 'w' ? '100vw' : undefined);\r\n\r\n return {\r\n src,\r\n ...(endSizes ? {sizes: endSizes} : {}),\r\n srcSet: srcSetEntries.join(', '),\r\n ...(width !== undefined ? {width} : {}),\r\n };\r\n};\r\n\r\nfunction buildSrcEntries(imageSrc: string, width: number, index: number, transformations: TransformationOptions, mode: NameMode, descriptor: 'w' | 'x', baseUrl?: string,): string {\r\n const url = buildTransformationUrl({\r\n src: imageSrc,\r\n baseUrl,\r\n transformations: {...transformations, width: width},\r\n mode\r\n }\r\n );\r\n\r\n const desc = descriptor === 'w' ? `${width}w` : `${index + 1}x`;\r\n\r\n return `${url} ${desc}`;\r\n}\r\n","import { Path, PathValue } from './util-types';\r\nimport { MAX_IMAGE_WIDTH } from './src-set';\r\n\r\nexport const normalizeHex = (value: string) => value.replace('#', '')\r\n\r\n\r\nexport const removeTrailingSlash = (baseUrl: string) => {\r\n return baseUrl.endsWith('/')\r\n ? baseUrl.slice(0, -1)\r\n : baseUrl;\r\n}\r\n\r\nexport const getNestedValue = <\r\n T,\r\n P extends Path<T>\r\n>(\r\n obj: T,\r\n path: P\r\n): PathValue<T, P> | undefined =>\r\n path.reduce<any>(\r\n (acc, key) => (acc == null ? undefined : acc[key]),\r\n obj\r\n );\r\n\r\n\r\nexport const clampMax = (v: number) => Math.min(v, MAX_IMAGE_WIDTH);\r\n\r\nexport const nearest = (sortedBreakpoints: number[],target: number) =>\r\n sortedBreakpoints.find(w => w >= target) ?? sortedBreakpoints[sortedBreakpoints.length - 1]!;\r\n\r\nexport const parseVW = (sizes?: string): number[] => {\r\n if (!sizes) return [];\r\n const foundSizes = sizes.match(/(\\d+(?:\\.\\d+)?)vw/g) || []\r\n\r\n return foundSizes.map((size) => parseInt(size, 10))\r\n};\r\n\r\n/**\r\n * Extracts the base URL path without query parameters\r\n */\r\nconst getBaseUrlWithoutQuery = (url: string | undefined): string => {\r\n if (!url) return '';\r\n const queryIndex = url.indexOf('?');\r\n return queryIndex === -1 ? url : url.slice(0, queryIndex);\r\n};\r\n\r\n/**\r\n * Checks if a URL is absolute (starts with http:// or https://)\r\n */\r\nconst isAbsoluteUrl = (url: string | undefined): boolean => {\r\n if (!url) return false;\r\n return url.startsWith('http://') || url.startsWith('https://');\r\n};\r\n\r\n\r\nexport function getFinalUrl(src: string, baseUrl?: string): string {\r\n const cleanSrc = getBaseUrlWithoutQuery(src);\r\n\r\n if (isAbsoluteUrl(cleanSrc)) {\r\n return removeTrailingSlash(cleanSrc);\r\n } else {\r\n // Relative path - combine with baseUrl\r\n const base = removeTrailingSlash(getBaseUrlWithoutQuery(baseUrl));\r\n\r\n // Ensure path has leading slash for proper URL construction\r\n const normalizedPath = normalizePath(cleanSrc)\r\n return `${base}${normalizedPath}`;\r\n }\r\n\r\n}\r\n\r\nexport function normalizePath(src: string): string {\r\n return src && !src.startsWith('/') ? `/${src}` : src;\r\n}\r\n\r\n\r\nexport const parseNumberLikeParam = (\r\n width: number | `${number}`\r\n): number => {\r\n if (typeof width === 'number') {\r\n return width;\r\n }\r\n\r\n const parsed = Number(width);\r\n\r\n if (!Number.isFinite(parsed)) {\r\n throw new Error(`Invalid width value: ${width}`);\r\n }\r\n\r\n return parsed;\r\n};\r\n","import { ParamSpec, Path } from './util-types';\r\nimport { TransformationOptions } from './types';\r\nimport { normalizeHex } from './utils';\r\n\r\nexport const PARAMS = [\r\n {path: ['width'], long: 'width', short: 'w'},\r\n {path: ['height'], long: 'height', short: 'h'},\r\n {path: ['dpr'], long: 'dpr', short: 'dpr'},\r\n {path: ['mode'], long: 'mode', short: 'rm'},\r\n {\r\n path: ['background'],\r\n long: 'background',\r\n short: 'bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n\r\n {path: ['format'], long: 'format', short: 'f'},\r\n {path: ['quality'], long: 'quality', short: 'q'},\r\n {path: ['stripMetadata'], long: 'stripMetadata', short: 'st'},\r\n\r\n {path: ['blur'], long: 'blur', short: 'bl'},\r\n {path: ['brightness'], long: 'brightness', short: 'br'},\r\n {path: ['contrast'], long: 'contrast', short: 'ct'},\r\n {path: ['grayscale'], long: 'grayscale', short: 'gs'},\r\n {path: ['saturation'], long: 'saturation', short: 'sa'},\r\n {path: ['sepia'], long: 'sepia', short: 'sp'},\r\n {path: ['sharpen'], long: 'sharpen', short: 'sh'},\r\n {path: ['noise'], long: 'noise', short: 'ns'},\r\n {path: ['rotate'], long: 'rotate', short: 'rt'},\r\n\r\n {path: ['border', 'width'], long: 'border.width', short: 'bo.w'},\r\n {\r\n path: ['border', 'color'],\r\n long: 'border.color',\r\n short: 'bo.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['border', 'radius'], long: 'border.radius', short: 'bo.r'},\r\n\r\n {path: ['crop', 'type'], long: 'crop.type', short: 'cr.t'},\r\n {path: ['crop', 'objectType'], long: 'crop.objectType', short: 'cr.o'},\r\n {path: ['crop', 'focus', 'position', 'x'], long: 'crop.x', short: 'cr.x'},\r\n {path: ['crop', 'focus', 'position', 'y'], long: 'crop.y', short: 'cr.y'},\r\n {path: ['crop', 'focus', 'strategy'], long: 'crop', short: 'cr'},\r\n {path: ['crop', 'width'], long: 'crop.width', short: 'cr.w'},\r\n {path: ['crop', 'height'], long: 'crop.height', short: 'cr.h'},\r\n {path: ['crop', 'afterResize'], long: 'crop.afterResize', short: 'cr.ar'},\r\n\r\n {path: ['text', 'text'], long: 'text', short: 'tx'},\r\n {\r\n path: ['text', 'color'],\r\n long: 'text.color',\r\n short: 'tx.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'opacity'], long: 'text.opacity', short: 'tx.o'},\r\n {path: ['text', 'size'], long: 'text.size', short: 'tx.s'},\r\n {path: ['text', 'font'], long: 'text.font', short: 'tx.f'},\r\n {path: ['text', 'fontSize'], long: 'text.fontSize', short: 'tx.fs'},\r\n {path: ['text', 'position'], long: 'text.position', short: 'tx.p'},\r\n {\r\n path: ['text', 'background'],\r\n long: 'text.background',\r\n short: 'tx.bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'backgroundOpacity'], long: 'text.backgroundOpacity', short: 'tx.bg.o'},\r\n\r\n {path: ['watermark', 'name'], long: 'watermark', short: 'wm'},\r\n {path: ['watermark', 'opacity'], long: 'watermark.opacity', short: 'wm.o'},\r\n {path: ['watermark', 'size'], long: 'watermark.size', short: 'wm.s'},\r\n {path: ['watermark', 'width'], long: 'watermark.width', short: 'wm.w'},\r\n {path: ['watermark', 'height'], long: 'watermark.height', short: 'wm.h'},\r\n {path: ['watermark', 'position'], long: 'watermark.position', short: 'wm.p'},\r\n] as const satisfies readonly ParamSpec<\r\n TransformationOptions,\r\n Path<TransformationOptions>\r\n>[];\r\n","import { BuildTransformationConfig, TransformationOptions } from './types';\r\nimport { getFinalUrl, getNestedValue } from './utils';\r\nimport { PARAMS } from './config';\r\n\r\n/**\r\n * Determines which parameter names to use in the query string.\r\n * - `'short'` - Use abbreviated aliases (e.g., `w`, `h`, `q`)\r\n * - `'long'` - Use full parameter names (e.g., `width`, `height`, `quality`)\r\n */\r\nexport type NameMode = 'short' | 'long';\r\n\r\n/**\r\n * Converts transformation options into a URL-encoded query string.\r\n *\r\n * Iterates through all defined parameters in the PARAMS config, extracts values\r\n * from the options object using their paths, applies any normalization functions\r\n * (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.\r\n *\r\n * @param options - The transformation options object\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns URL-encoded query string (without leading `?`)\r\n *\r\n * @example\r\n * ```ts\r\n * // Using short aliases (default)\r\n * buildQueryParams({ width: 800, height: 600, quality: 80 });\r\n * // => 'w=800&h=600&q=80'\r\n *\r\n * // Using long parameter names\r\n * buildQueryParams({ width: 800, format: 'webp' }, 'long');\r\n * // => 'width=800&format=webp'\r\n *\r\n * // Nested options\r\n * buildQueryParams({ border: { width: 2, color: '#ff0000' } });\r\n * // => 'bo.w=2&bo.c=ff0000'\r\n * ```\r\n */\r\nexport const buildTransformationQueryParams = (\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n): string => {\r\n const params: Array<[string, string]> = [];\r\n\r\n for (const spec of PARAMS) {\r\n const value = getNestedValue(options, spec.path);\r\n if (value == null) continue;\r\n\r\n const name =\r\n mode === 'short' && spec.short ? spec.short : spec.long;\r\n\r\n let normalized = value\r\n if ('normalize' in spec) {\r\n normalized = spec.normalize(value)\r\n }\r\n\r\n params.push([name, String(normalized)]);\r\n }\r\n\r\n\r\n return new URLSearchParams(\r\n params.map(([k, v]) => [k, v])\r\n ).toString();\r\n\r\n};\r\n\r\n/**\r\n * Builds a complete transformation URL from a base URL and transformation options.\r\n *\r\n * Combines the configured base URL with query parameters generated from the\r\n * transformation options. Handles the special `original` flag which returns\r\n * the unmodified source image.\r\n *\r\n * @param src - Absolute or relative path to your image\r\n * @param config - Configuration containing the base URL for the image\r\n * @param transformations - The transformation options to apply\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns The complete URL string with query parameters\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/images/photo.jpg' };\r\n *\r\n * // Basic resize\r\n * buildTransformationUrl(config, { width: 400, height: 300 });\r\n * // => 'https://example.com/images/photo.jpg?w=400&h=300'\r\n *\r\n * // Multiple transformations\r\n * buildTransformationUrl(config, {\r\n * width: 800,\r\n * format: 'webp',\r\n * quality: 85,\r\n * blur: 5\r\n * });\r\n * // => 'https://example.com/images/photo.jpg?w=800&f=webp&q=85&bl=5'\r\n *\r\n * // Request original image (no transformations)\r\n * buildTransformationUrl(config, { original: true });\r\n * // => 'https://example.com/images/photo.jpg?original'\r\n *\r\n * // Using long parameter names\r\n * buildTransformationUrl(config, { width: 400 }, 'long');\r\n * // => 'https://example.com/images/photo.jpg?width=400'\r\n * ```\r\n */\r\nexport const buildTransformationUrl = (\r\n {src, transformations = {}, baseUrl, mode = 'short'}: BuildTransformationConfig\r\n) => {\r\n const url = getFinalUrl(src, baseUrl)\r\n\r\n if (transformations.original) {\r\n return `${url}?original`;\r\n }\r\n\r\n const query = buildTransformationQueryParams(transformations, mode);\r\n\r\n return `${url}?${query}`;\r\n};\r\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Determines which parameter names to use in the query string.
|
|
3
|
+
* - `'short'` - Use abbreviated aliases (e.g., `w`, `h`, `q`)
|
|
4
|
+
* - `'long'` - Use full parameter names (e.g., `width`, `height`, `quality`)
|
|
5
|
+
*/
|
|
6
|
+
type NameMode = 'short' | 'long';
|
|
7
|
+
/**
|
|
8
|
+
* Converts transformation options into a URL-encoded query string.
|
|
9
|
+
*
|
|
10
|
+
* Iterates through all defined parameters in the PARAMS config, extracts values
|
|
11
|
+
* from the options object using their paths, applies any normalization functions
|
|
12
|
+
* (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.
|
|
13
|
+
*
|
|
14
|
+
* @param options - The transformation options object
|
|
15
|
+
* @param mode - Whether to use short aliases or long parameter names (default: `'short'`)
|
|
16
|
+
* @returns URL-encoded query string (without leading `?`)
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // Using short aliases (default)
|
|
21
|
+
* buildQueryParams({ width: 800, height: 600, quality: 80 });
|
|
22
|
+
* // => 'w=800&h=600&q=80'
|
|
23
|
+
*
|
|
24
|
+
* // Using long parameter names
|
|
25
|
+
* buildQueryParams({ width: 800, format: 'webp' }, 'long');
|
|
26
|
+
* // => 'width=800&format=webp'
|
|
27
|
+
*
|
|
28
|
+
* // Nested options
|
|
29
|
+
* buildQueryParams({ border: { width: 2, color: '#ff0000' } });
|
|
30
|
+
* // => 'bo.w=2&bo.c=ff0000'
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare const buildTransformationQueryParams: (options: TransformationOptions, mode?: NameMode) => string;
|
|
34
|
+
/**
|
|
35
|
+
* Builds a complete transformation URL from a base URL and transformation options.
|
|
36
|
+
*
|
|
37
|
+
* Combines the configured base URL with query parameters generated from the
|
|
38
|
+
* transformation options. Handles the special `original` flag which returns
|
|
39
|
+
* the unmodified source image.
|
|
40
|
+
*
|
|
41
|
+
* @param src - Absolute or relative path to your image
|
|
42
|
+
* @param config - Configuration containing the base URL for the image
|
|
43
|
+
* @param transformations - The transformation options to apply
|
|
44
|
+
* @param mode - Whether to use short aliases or long parameter names (default: `'short'`)
|
|
45
|
+
* @returns The complete URL string with query parameters
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const config = { baseUrl: 'https://example.com/images/photo.jpg' };
|
|
50
|
+
*
|
|
51
|
+
* // Basic resize
|
|
52
|
+
* buildTransformationUrl(config, { width: 400, height: 300 });
|
|
53
|
+
* // => 'https://example.com/images/photo.jpg?w=400&h=300'
|
|
54
|
+
*
|
|
55
|
+
* // Multiple transformations
|
|
56
|
+
* buildTransformationUrl(config, {
|
|
57
|
+
* width: 800,
|
|
58
|
+
* format: 'webp',
|
|
59
|
+
* quality: 85,
|
|
60
|
+
* blur: 5
|
|
61
|
+
* });
|
|
62
|
+
* // => 'https://example.com/images/photo.jpg?w=800&f=webp&q=85&bl=5'
|
|
63
|
+
*
|
|
64
|
+
* // Request original image (no transformations)
|
|
65
|
+
* buildTransformationUrl(config, { original: true });
|
|
66
|
+
* // => 'https://example.com/images/photo.jpg?original'
|
|
67
|
+
*
|
|
68
|
+
* // Using long parameter names
|
|
69
|
+
* buildTransformationUrl(config, { width: 400 }, 'long');
|
|
70
|
+
* // => 'https://example.com/images/photo.jpg?width=400'
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
declare const buildTransformationUrl: ({ src, transformations, baseUrl, mode }: BuildTransformationConfig) => string;
|
|
74
|
+
|
|
1
75
|
/**
|
|
2
76
|
* Helper type to extract values from const objects
|
|
3
77
|
*/
|
|
@@ -237,8 +311,8 @@ interface TransformationOptions extends ResizeOptions, FormatOptions, EffectOpti
|
|
|
237
311
|
interface PixelFiddlerConfig {
|
|
238
312
|
/** Base URL for the CDN (defaults to https://cdn.pixel-fiddler.com) */
|
|
239
313
|
baseUrl?: string;
|
|
240
|
-
/**
|
|
241
|
-
|
|
314
|
+
/** Max DPR that you want responsive srcSet to support, defaults to 2 */
|
|
315
|
+
maxDpr?: number;
|
|
242
316
|
}
|
|
243
317
|
/**
|
|
244
318
|
* URL builder configuration (internal)
|
|
@@ -247,41 +321,12 @@ interface UrlBuilderConfig extends PixelFiddlerConfig {
|
|
|
247
321
|
/** Resolved base URL */
|
|
248
322
|
baseUrl: string;
|
|
249
323
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
type NameMode = 'short' | 'long';
|
|
257
|
-
/**
|
|
258
|
-
* Converts transformation options into a URL-encoded query string.
|
|
259
|
-
*
|
|
260
|
-
* Iterates through all defined parameters in the PARAMS config, extracts values
|
|
261
|
-
* from the options object using their paths, applies any normalization functions
|
|
262
|
-
* (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.
|
|
263
|
-
*
|
|
264
|
-
* @param options - The transformation options object
|
|
265
|
-
* @param mode - Whether to use short aliases or long parameter names (default: `'short'`)
|
|
266
|
-
* @returns URL-encoded query string (without leading `?`)
|
|
267
|
-
*
|
|
268
|
-
* @example
|
|
269
|
-
* ```ts
|
|
270
|
-
* // Using short aliases (default)
|
|
271
|
-
* buildQueryParams({ width: 800, height: 600, quality: 80 });
|
|
272
|
-
* // => 'w=800&h=600&q=80'
|
|
273
|
-
*
|
|
274
|
-
* // Using long parameter names
|
|
275
|
-
* buildQueryParams({ width: 800, format: 'webp' }, 'long');
|
|
276
|
-
* // => 'width=800&format=webp'
|
|
277
|
-
*
|
|
278
|
-
* // Nested options
|
|
279
|
-
* buildQueryParams({ border: { width: 2, color: '#ff0000' } });
|
|
280
|
-
* // => 'bo.w=2&bo.c=ff0000'
|
|
281
|
-
* ```
|
|
282
|
-
*/
|
|
283
|
-
declare const buildTransformationQueryParams: (options: TransformationOptions, mode?: NameMode) => string;
|
|
284
|
-
declare const buildTransformationUrl: (config: UrlBuilderConfig, options: TransformationOptions, mode?: NameMode) => string;
|
|
324
|
+
type BuildTransformationConfig = {
|
|
325
|
+
src: string;
|
|
326
|
+
transformations?: TransformationOptions;
|
|
327
|
+
baseUrl?: string;
|
|
328
|
+
mode?: NameMode;
|
|
329
|
+
};
|
|
285
330
|
|
|
286
331
|
/**
|
|
287
332
|
* Options for responsive image attributes generation
|
|
@@ -305,6 +350,7 @@ interface ResponsiveImageOptions {
|
|
|
305
350
|
transformations?: TransformationOptions;
|
|
306
351
|
/** Whether to use short or long parameter names (default: 'short') */
|
|
307
352
|
mode?: NameMode;
|
|
353
|
+
maxDpr?: number;
|
|
308
354
|
}
|
|
309
355
|
/**
|
|
310
356
|
* Result of responsive image attributes generation
|
|
@@ -328,7 +374,8 @@ interface ResponsiveImageResult {
|
|
|
328
374
|
* 2. **Fixed width (with `width`)**: Uses DPR descriptors (`x`) for fixed-size
|
|
329
375
|
* images that need high-DPI support.
|
|
330
376
|
*
|
|
331
|
-
* @param
|
|
377
|
+
* @param imageSrc - Relative or an absolute path to the image
|
|
378
|
+
* @param baseUrl - This should be your baseURL endpoint for the source from PixelFiddle Dashboard.
|
|
332
379
|
* @param options - Options for responsive image generation
|
|
333
380
|
* @returns Object containing src, srcSet, and optionally sizes attributes
|
|
334
381
|
*
|
|
@@ -359,6 +406,8 @@ interface ResponsiveImageResult {
|
|
|
359
406
|
* });
|
|
360
407
|
* ```
|
|
361
408
|
*/
|
|
362
|
-
declare const createResponsiveAttributes: (
|
|
409
|
+
declare const createResponsiveAttributes: (imageSrc: string, baseUrl?: string, options?: ResponsiveImageOptions) => ResponsiveImageResult;
|
|
410
|
+
|
|
411
|
+
declare const parseNumberLikeParam: (width: number | `${number}`) => number;
|
|
363
412
|
|
|
364
|
-
export { type AutoCropFocus, type BorderOptions, type CropFocus, type CropFocusOptions, type CropFocusPosition, type CropFocusStrategy, type CropObjectType, type CropOptions, type CropType, type DevicePixelRatio, type EffectOptions, type FormatOptions, type HexColor, type ImageFormat, type ObjectCropOptions, type PixelFiddlerConfig, type Position, type ResizeMode, type ResizeOptions, type RotationAngle, type SimpleCropOptions, type SpecifiedCropFocus, type TextOptions, type TransformationOptions, type UrlBuilderConfig, type WatermarkOptions, buildTransformationQueryParams, buildTransformationUrl, createResponsiveAttributes };
|
|
413
|
+
export { type AutoCropFocus, type BorderOptions, type BuildTransformationConfig, type CropFocus, type CropFocusOptions, type CropFocusPosition, type CropFocusStrategy, type CropObjectType, type CropOptions, type CropType, type DevicePixelRatio, type EffectOptions, type FormatOptions, type HexColor, type ImageFormat, type ObjectCropOptions, type PixelFiddlerConfig, type Position, type ResizeMode, type ResizeOptions, type RotationAngle, type SimpleCropOptions, type SpecifiedCropFocus, type TextOptions, type TransformationOptions, type UrlBuilderConfig, type WatermarkOptions, buildTransformationQueryParams, buildTransformationUrl, createResponsiveAttributes, parseNumberLikeParam };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Determines which parameter names to use in the query string.
|
|
3
|
+
* - `'short'` - Use abbreviated aliases (e.g., `w`, `h`, `q`)
|
|
4
|
+
* - `'long'` - Use full parameter names (e.g., `width`, `height`, `quality`)
|
|
5
|
+
*/
|
|
6
|
+
type NameMode = 'short' | 'long';
|
|
7
|
+
/**
|
|
8
|
+
* Converts transformation options into a URL-encoded query string.
|
|
9
|
+
*
|
|
10
|
+
* Iterates through all defined parameters in the PARAMS config, extracts values
|
|
11
|
+
* from the options object using their paths, applies any normalization functions
|
|
12
|
+
* (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.
|
|
13
|
+
*
|
|
14
|
+
* @param options - The transformation options object
|
|
15
|
+
* @param mode - Whether to use short aliases or long parameter names (default: `'short'`)
|
|
16
|
+
* @returns URL-encoded query string (without leading `?`)
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // Using short aliases (default)
|
|
21
|
+
* buildQueryParams({ width: 800, height: 600, quality: 80 });
|
|
22
|
+
* // => 'w=800&h=600&q=80'
|
|
23
|
+
*
|
|
24
|
+
* // Using long parameter names
|
|
25
|
+
* buildQueryParams({ width: 800, format: 'webp' }, 'long');
|
|
26
|
+
* // => 'width=800&format=webp'
|
|
27
|
+
*
|
|
28
|
+
* // Nested options
|
|
29
|
+
* buildQueryParams({ border: { width: 2, color: '#ff0000' } });
|
|
30
|
+
* // => 'bo.w=2&bo.c=ff0000'
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare const buildTransformationQueryParams: (options: TransformationOptions, mode?: NameMode) => string;
|
|
34
|
+
/**
|
|
35
|
+
* Builds a complete transformation URL from a base URL and transformation options.
|
|
36
|
+
*
|
|
37
|
+
* Combines the configured base URL with query parameters generated from the
|
|
38
|
+
* transformation options. Handles the special `original` flag which returns
|
|
39
|
+
* the unmodified source image.
|
|
40
|
+
*
|
|
41
|
+
* @param src - Absolute or relative path to your image
|
|
42
|
+
* @param config - Configuration containing the base URL for the image
|
|
43
|
+
* @param transformations - The transformation options to apply
|
|
44
|
+
* @param mode - Whether to use short aliases or long parameter names (default: `'short'`)
|
|
45
|
+
* @returns The complete URL string with query parameters
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const config = { baseUrl: 'https://example.com/images/photo.jpg' };
|
|
50
|
+
*
|
|
51
|
+
* // Basic resize
|
|
52
|
+
* buildTransformationUrl(config, { width: 400, height: 300 });
|
|
53
|
+
* // => 'https://example.com/images/photo.jpg?w=400&h=300'
|
|
54
|
+
*
|
|
55
|
+
* // Multiple transformations
|
|
56
|
+
* buildTransformationUrl(config, {
|
|
57
|
+
* width: 800,
|
|
58
|
+
* format: 'webp',
|
|
59
|
+
* quality: 85,
|
|
60
|
+
* blur: 5
|
|
61
|
+
* });
|
|
62
|
+
* // => 'https://example.com/images/photo.jpg?w=800&f=webp&q=85&bl=5'
|
|
63
|
+
*
|
|
64
|
+
* // Request original image (no transformations)
|
|
65
|
+
* buildTransformationUrl(config, { original: true });
|
|
66
|
+
* // => 'https://example.com/images/photo.jpg?original'
|
|
67
|
+
*
|
|
68
|
+
* // Using long parameter names
|
|
69
|
+
* buildTransformationUrl(config, { width: 400 }, 'long');
|
|
70
|
+
* // => 'https://example.com/images/photo.jpg?width=400'
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
declare const buildTransformationUrl: ({ src, transformations, baseUrl, mode }: BuildTransformationConfig) => string;
|
|
74
|
+
|
|
1
75
|
/**
|
|
2
76
|
* Helper type to extract values from const objects
|
|
3
77
|
*/
|
|
@@ -237,8 +311,8 @@ interface TransformationOptions extends ResizeOptions, FormatOptions, EffectOpti
|
|
|
237
311
|
interface PixelFiddlerConfig {
|
|
238
312
|
/** Base URL for the CDN (defaults to https://cdn.pixel-fiddler.com) */
|
|
239
313
|
baseUrl?: string;
|
|
240
|
-
/**
|
|
241
|
-
|
|
314
|
+
/** Max DPR that you want responsive srcSet to support, defaults to 2 */
|
|
315
|
+
maxDpr?: number;
|
|
242
316
|
}
|
|
243
317
|
/**
|
|
244
318
|
* URL builder configuration (internal)
|
|
@@ -247,41 +321,12 @@ interface UrlBuilderConfig extends PixelFiddlerConfig {
|
|
|
247
321
|
/** Resolved base URL */
|
|
248
322
|
baseUrl: string;
|
|
249
323
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
type NameMode = 'short' | 'long';
|
|
257
|
-
/**
|
|
258
|
-
* Converts transformation options into a URL-encoded query string.
|
|
259
|
-
*
|
|
260
|
-
* Iterates through all defined parameters in the PARAMS config, extracts values
|
|
261
|
-
* from the options object using their paths, applies any normalization functions
|
|
262
|
-
* (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.
|
|
263
|
-
*
|
|
264
|
-
* @param options - The transformation options object
|
|
265
|
-
* @param mode - Whether to use short aliases or long parameter names (default: `'short'`)
|
|
266
|
-
* @returns URL-encoded query string (without leading `?`)
|
|
267
|
-
*
|
|
268
|
-
* @example
|
|
269
|
-
* ```ts
|
|
270
|
-
* // Using short aliases (default)
|
|
271
|
-
* buildQueryParams({ width: 800, height: 600, quality: 80 });
|
|
272
|
-
* // => 'w=800&h=600&q=80'
|
|
273
|
-
*
|
|
274
|
-
* // Using long parameter names
|
|
275
|
-
* buildQueryParams({ width: 800, format: 'webp' }, 'long');
|
|
276
|
-
* // => 'width=800&format=webp'
|
|
277
|
-
*
|
|
278
|
-
* // Nested options
|
|
279
|
-
* buildQueryParams({ border: { width: 2, color: '#ff0000' } });
|
|
280
|
-
* // => 'bo.w=2&bo.c=ff0000'
|
|
281
|
-
* ```
|
|
282
|
-
*/
|
|
283
|
-
declare const buildTransformationQueryParams: (options: TransformationOptions, mode?: NameMode) => string;
|
|
284
|
-
declare const buildTransformationUrl: (config: UrlBuilderConfig, options: TransformationOptions, mode?: NameMode) => string;
|
|
324
|
+
type BuildTransformationConfig = {
|
|
325
|
+
src: string;
|
|
326
|
+
transformations?: TransformationOptions;
|
|
327
|
+
baseUrl?: string;
|
|
328
|
+
mode?: NameMode;
|
|
329
|
+
};
|
|
285
330
|
|
|
286
331
|
/**
|
|
287
332
|
* Options for responsive image attributes generation
|
|
@@ -305,6 +350,7 @@ interface ResponsiveImageOptions {
|
|
|
305
350
|
transformations?: TransformationOptions;
|
|
306
351
|
/** Whether to use short or long parameter names (default: 'short') */
|
|
307
352
|
mode?: NameMode;
|
|
353
|
+
maxDpr?: number;
|
|
308
354
|
}
|
|
309
355
|
/**
|
|
310
356
|
* Result of responsive image attributes generation
|
|
@@ -328,7 +374,8 @@ interface ResponsiveImageResult {
|
|
|
328
374
|
* 2. **Fixed width (with `width`)**: Uses DPR descriptors (`x`) for fixed-size
|
|
329
375
|
* images that need high-DPI support.
|
|
330
376
|
*
|
|
331
|
-
* @param
|
|
377
|
+
* @param imageSrc - Relative or an absolute path to the image
|
|
378
|
+
* @param baseUrl - This should be your baseURL endpoint for the source from PixelFiddle Dashboard.
|
|
332
379
|
* @param options - Options for responsive image generation
|
|
333
380
|
* @returns Object containing src, srcSet, and optionally sizes attributes
|
|
334
381
|
*
|
|
@@ -359,6 +406,8 @@ interface ResponsiveImageResult {
|
|
|
359
406
|
* });
|
|
360
407
|
* ```
|
|
361
408
|
*/
|
|
362
|
-
declare const createResponsiveAttributes: (
|
|
409
|
+
declare const createResponsiveAttributes: (imageSrc: string, baseUrl?: string, options?: ResponsiveImageOptions) => ResponsiveImageResult;
|
|
410
|
+
|
|
411
|
+
declare const parseNumberLikeParam: (width: number | `${number}`) => number;
|
|
363
412
|
|
|
364
|
-
export { type AutoCropFocus, type BorderOptions, type CropFocus, type CropFocusOptions, type CropFocusPosition, type CropFocusStrategy, type CropObjectType, type CropOptions, type CropType, type DevicePixelRatio, type EffectOptions, type FormatOptions, type HexColor, type ImageFormat, type ObjectCropOptions, type PixelFiddlerConfig, type Position, type ResizeMode, type ResizeOptions, type RotationAngle, type SimpleCropOptions, type SpecifiedCropFocus, type TextOptions, type TransformationOptions, type UrlBuilderConfig, type WatermarkOptions, buildTransformationQueryParams, buildTransformationUrl, createResponsiveAttributes };
|
|
413
|
+
export { type AutoCropFocus, type BorderOptions, type BuildTransformationConfig, type CropFocus, type CropFocusOptions, type CropFocusPosition, type CropFocusStrategy, type CropObjectType, type CropOptions, type CropType, type DevicePixelRatio, type EffectOptions, type FormatOptions, type HexColor, type ImageFormat, type ObjectCropOptions, type PixelFiddlerConfig, type Position, type ResizeMode, type ResizeOptions, type RotationAngle, type SimpleCropOptions, type SpecifiedCropFocus, type TextOptions, type TransformationOptions, type UrlBuilderConfig, type WatermarkOptions, buildTransformationQueryParams, buildTransformationUrl, createResponsiveAttributes, parseNumberLikeParam };
|
package/dist/index.js
CHANGED
|
@@ -8,31 +8,32 @@ var DEFAULT_DEVICE_BREAKPOINTS = [
|
|
|
8
8
|
1024,
|
|
9
9
|
1280,
|
|
10
10
|
1536,
|
|
11
|
-
1920
|
|
11
|
+
1920,
|
|
12
|
+
2048,
|
|
13
|
+
3840
|
|
12
14
|
];
|
|
13
15
|
var DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384];
|
|
14
16
|
var DEFAULT_MAX_DPR = 2;
|
|
15
|
-
function
|
|
17
|
+
function calculatesCandidateWidths(params) {
|
|
16
18
|
const {
|
|
17
19
|
allBreakpoints,
|
|
18
20
|
deviceBreakpoints,
|
|
19
21
|
explicitWidth,
|
|
20
|
-
sizesAttr
|
|
22
|
+
sizesAttr,
|
|
23
|
+
maxDpr
|
|
21
24
|
} = params;
|
|
22
|
-
|
|
23
|
-
const sortedDevice = [...new Set(deviceBreakpoints)].sort((a, b) => a - b);
|
|
24
|
-
if (!sortedAll.length || !sortedDevice.length) {
|
|
25
|
+
if (!allBreakpoints.length || !deviceBreakpoints.length) {
|
|
25
26
|
return { candidates: [], descriptor: "w" };
|
|
26
27
|
}
|
|
27
|
-
const minDevice =
|
|
28
|
-
const maxDevice =
|
|
28
|
+
const minDevice = deviceBreakpoints[0];
|
|
29
|
+
const maxDevice = deviceBreakpoints[deviceBreakpoints.length - 1];
|
|
29
30
|
const vwRatios = parseVW(sizesAttr);
|
|
30
31
|
const hasVW = vwRatios.length > 0;
|
|
31
|
-
if (
|
|
32
|
-
const DPRs = [1, 2, 3];
|
|
32
|
+
if (explicitWidth && !hasVW) {
|
|
33
|
+
const DPRs = [1, 2, 3, 4].filter((item) => item <= maxDpr);
|
|
33
34
|
const candidates = Array.from(
|
|
34
35
|
new Set(
|
|
35
|
-
DPRs.map((dpr) => nearest(
|
|
36
|
+
DPRs.map((dpr) => nearest(allBreakpoints, explicitWidth * dpr)).map(clampMax)
|
|
36
37
|
)
|
|
37
38
|
).filter((w) => w <= MAX_IMAGE_WIDTH);
|
|
38
39
|
return {
|
|
@@ -41,52 +42,56 @@ function computeCandidateWidths(params) {
|
|
|
41
42
|
};
|
|
42
43
|
}
|
|
43
44
|
if (hasVW) {
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const minPx = minDevice * minRatio;
|
|
47
|
-
const maxPx = maxDevice * maxRatio * DEFAULT_MAX_DPR;
|
|
48
|
-
const candidates = sortedAll.filter(
|
|
49
|
-
(w) => w >= minPx && w <= clampMax(maxPx)
|
|
50
|
-
);
|
|
45
|
+
const lowestRatio = Math.min(...vwRatios) / 100;
|
|
46
|
+
const minRequiredPx = minDevice * lowestRatio;
|
|
51
47
|
return {
|
|
52
|
-
|
|
48
|
+
// Responsive layout only uses device breakpoints
|
|
49
|
+
candidates: deviceBreakpoints.filter((w) => w >= minRequiredPx && w <= MAX_IMAGE_WIDTH),
|
|
53
50
|
descriptor: "w"
|
|
54
51
|
};
|
|
55
52
|
}
|
|
56
|
-
const
|
|
53
|
+
const upperLimit = maxDevice * maxDpr;
|
|
57
54
|
return {
|
|
58
|
-
candidates:
|
|
55
|
+
candidates: allBreakpoints.filter((w) => w <= upperLimit),
|
|
59
56
|
descriptor: "w"
|
|
60
57
|
};
|
|
61
58
|
}
|
|
62
|
-
var createResponsiveAttributes = (
|
|
59
|
+
var createResponsiveAttributes = (imageSrc, baseUrl, options) => {
|
|
63
60
|
const {
|
|
64
61
|
sizes,
|
|
65
62
|
width,
|
|
66
63
|
deviceBreakpoints = [...DEFAULT_DEVICE_BREAKPOINTS],
|
|
67
64
|
imageBreakpoints = [...DEFAULT_IMAGE_BREAKPOINTS],
|
|
68
65
|
transformations = {},
|
|
69
|
-
mode = "short"
|
|
70
|
-
|
|
66
|
+
mode = "short",
|
|
67
|
+
maxDpr = DEFAULT_MAX_DPR
|
|
68
|
+
} = options || {};
|
|
71
69
|
const baseWidth = transformations.width ?? width;
|
|
72
70
|
const sortedDeviceBreakpoints = [...deviceBreakpoints].sort((a, b) => a - b);
|
|
73
71
|
const sortedImageBreakpoints = [...imageBreakpoints].sort((a, b) => a - b);
|
|
74
72
|
const allBreakpoints = [.../* @__PURE__ */ new Set([...sortedImageBreakpoints, ...sortedDeviceBreakpoints])].sort((a, b) => a - b);
|
|
75
|
-
const { candidates, descriptor } =
|
|
73
|
+
const { candidates, descriptor } = calculatesCandidateWidths({
|
|
76
74
|
allBreakpoints,
|
|
77
75
|
deviceBreakpoints: sortedDeviceBreakpoints,
|
|
78
76
|
...baseWidth !== void 0 && { explicitWidth: baseWidth },
|
|
79
|
-
...sizes !== void 0 && { sizesAttr: sizes }
|
|
77
|
+
...sizes !== void 0 && { sizesAttr: sizes },
|
|
78
|
+
maxDpr
|
|
80
79
|
});
|
|
81
80
|
if (candidates.length === 0) {
|
|
82
|
-
return { src:
|
|
81
|
+
return { src: getFinalUrl(imageSrc, baseUrl) };
|
|
83
82
|
}
|
|
84
|
-
const srcSetEntries = candidates.map((w, index) => buildSrcEntries(w, index,
|
|
83
|
+
const srcSetEntries = candidates.map((w, index) => buildSrcEntries(imageSrc, w, index, transformations, mode, descriptor, baseUrl));
|
|
85
84
|
const largestWidth = candidates[candidates.length - 1];
|
|
86
85
|
const src = buildTransformationUrl(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
{
|
|
87
|
+
src: imageSrc,
|
|
88
|
+
baseUrl,
|
|
89
|
+
transformations: {
|
|
90
|
+
...transformations,
|
|
91
|
+
width: largestWidth
|
|
92
|
+
},
|
|
93
|
+
mode
|
|
94
|
+
}
|
|
90
95
|
);
|
|
91
96
|
const endSizes = sizes ?? (descriptor === "w" ? "100vw" : void 0);
|
|
92
97
|
return {
|
|
@@ -96,11 +101,14 @@ var createResponsiveAttributes = (config, options = {}) => {
|
|
|
96
101
|
...width !== void 0 ? { width } : {}
|
|
97
102
|
};
|
|
98
103
|
};
|
|
99
|
-
function buildSrcEntries(width, index,
|
|
104
|
+
function buildSrcEntries(imageSrc, width, index, transformations, mode, descriptor, baseUrl) {
|
|
100
105
|
const url = buildTransformationUrl(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
{
|
|
107
|
+
src: imageSrc,
|
|
108
|
+
baseUrl,
|
|
109
|
+
transformations: { ...transformations, width },
|
|
110
|
+
mode
|
|
111
|
+
}
|
|
104
112
|
);
|
|
105
113
|
const desc = descriptor === "w" ? `${width}w` : `${index + 1}x`;
|
|
106
114
|
return `${url} ${desc}`;
|
|
@@ -119,9 +127,40 @@ var clampMax = (v) => Math.min(v, MAX_IMAGE_WIDTH);
|
|
|
119
127
|
var nearest = (sortedBreakpoints, target) => sortedBreakpoints.find((w) => w >= target) ?? sortedBreakpoints[sortedBreakpoints.length - 1];
|
|
120
128
|
var parseVW = (sizes) => {
|
|
121
129
|
if (!sizes) return [];
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
130
|
+
const foundSizes = sizes.match(/(\d+(?:\.\d+)?)vw/g) || [];
|
|
131
|
+
return foundSizes.map((size) => parseInt(size, 10));
|
|
132
|
+
};
|
|
133
|
+
var getBaseUrlWithoutQuery = (url) => {
|
|
134
|
+
if (!url) return "";
|
|
135
|
+
const queryIndex = url.indexOf("?");
|
|
136
|
+
return queryIndex === -1 ? url : url.slice(0, queryIndex);
|
|
137
|
+
};
|
|
138
|
+
var isAbsoluteUrl = (url) => {
|
|
139
|
+
if (!url) return false;
|
|
140
|
+
return url.startsWith("http://") || url.startsWith("https://");
|
|
141
|
+
};
|
|
142
|
+
function getFinalUrl(src, baseUrl) {
|
|
143
|
+
const cleanSrc = getBaseUrlWithoutQuery(src);
|
|
144
|
+
if (isAbsoluteUrl(cleanSrc)) {
|
|
145
|
+
return removeTrailingSlash(cleanSrc);
|
|
146
|
+
} else {
|
|
147
|
+
const base = removeTrailingSlash(getBaseUrlWithoutQuery(baseUrl));
|
|
148
|
+
const normalizedPath = normalizePath(cleanSrc);
|
|
149
|
+
return `${base}${normalizedPath}`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function normalizePath(src) {
|
|
153
|
+
return src && !src.startsWith("/") ? `/${src}` : src;
|
|
154
|
+
}
|
|
155
|
+
var parseNumberLikeParam = (width) => {
|
|
156
|
+
if (typeof width === "number") {
|
|
157
|
+
return width;
|
|
158
|
+
}
|
|
159
|
+
const parsed = Number(width);
|
|
160
|
+
if (!Number.isFinite(parsed)) {
|
|
161
|
+
throw new Error(`Invalid width value: ${width}`);
|
|
162
|
+
}
|
|
163
|
+
return parsed;
|
|
125
164
|
};
|
|
126
165
|
|
|
127
166
|
// src/config.ts
|
|
@@ -208,19 +247,15 @@ var buildTransformationQueryParams = (options, mode = "short") => {
|
|
|
208
247
|
params.map(([k, v]) => [k, v])
|
|
209
248
|
).toString();
|
|
210
249
|
};
|
|
211
|
-
var
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
var buildTransformationUrl = (config, options, mode = "short") => {
|
|
216
|
-
const baseUrl = removeTrailingSlash(getBaseUrlWithoutQuery(config.baseUrl));
|
|
217
|
-
if (options.original) {
|
|
218
|
-
return `${baseUrl}?original`;
|
|
250
|
+
var buildTransformationUrl = ({ src, transformations = {}, baseUrl, mode = "short" }) => {
|
|
251
|
+
const url = getFinalUrl(src, baseUrl);
|
|
252
|
+
if (transformations.original) {
|
|
253
|
+
return `${url}?original`;
|
|
219
254
|
}
|
|
220
|
-
const query = buildTransformationQueryParams(
|
|
221
|
-
return `${
|
|
255
|
+
const query = buildTransformationQueryParams(transformations, mode);
|
|
256
|
+
return `${url}?${query}`;
|
|
222
257
|
};
|
|
223
258
|
|
|
224
|
-
export { buildTransformationQueryParams, buildTransformationUrl, createResponsiveAttributes };
|
|
259
|
+
export { buildTransformationQueryParams, buildTransformationUrl, createResponsiveAttributes, parseNumberLikeParam };
|
|
225
260
|
//# sourceMappingURL=index.js.map
|
|
226
261
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/src-set.ts","../src/utils.ts","../src/config.ts","../src/url-builder.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { buildTransformationUrl, NameMode } from './url-builder';\r\nimport { TransformationOptions, UrlBuilderConfig } from './types';\r\nimport { clampMax, nearest, parseVW } from './utils';\r\n\r\n/**\r\n * Maximum supported image width\r\n */\r\nexport const MAX_IMAGE_WIDTH = 4096;\r\n\r\n/**\r\n * Default device breakpoints for responsive srcset generation\r\n */\r\nexport const DEFAULT_DEVICE_BREAKPOINTS = [\r\n 320,\r\n 375,\r\n 640,\r\n 768,\r\n 1024,\r\n 1280,\r\n 1536,\r\n 1920\r\n] as const;\r\n\r\n/**\r\n * Default image breakpoints for smaller images (icons, thumbnails)\r\n */\r\nexport const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const;\r\n\r\n/**\r\n * Options for responsive image attributes generation\r\n */\r\nexport interface ResponsiveImageOptions {\r\n /**\r\n * The `sizes` attribute value (e.g., \"100vw\", \"(max-width: 768px) 100vw, 50vw\").\r\n * When provided, generates width descriptors (`w`) for responsive layouts.\r\n */\r\n sizes?: string;\r\n /**\r\n * Fixed display width in CSS pixels.\r\n * When provided without `sizes`, generates DPR descriptors (`x`).\r\n */\r\n width?: number;\r\n /** Device breakpoints for viewport-based sizing (default: DEFAULT_DEVICE_BREAKPOINTS) */\r\n deviceBreakpoints?: number[];\r\n /** Image breakpoints for smaller images (default: DEFAULT_IMAGE_BREAKPOINTS) */\r\n imageBreakpoints?: number[];\r\n /** Base transformation options to apply to all variants */\r\n transformations?: TransformationOptions;\r\n /** Whether to use short or long parameter names (default: 'short') */\r\n mode?: NameMode;\r\n}\r\n\r\n/**\r\n * Result of responsive image attributes generation\r\n */\r\nexport interface ResponsiveImageResult {\r\n /** The src attribute value (largest/default variant) */\r\n src: string;\r\n /** The srcset attribute value */\r\n srcSet?: string;\r\n /** The sizes attribute value (for width descriptors) */\r\n sizes?: string;\r\n /** The width attribute value (when explicitly provided) */\r\n width?: number;\r\n}\r\n\r\n/**\r\n * Computes candidate widths and descriptor type based on input options.\r\n */\r\nconst DEFAULT_MAX_DPR = 2;\r\n\r\nfunction computeCandidateWidths(params: {\r\n allBreakpoints: number[];\r\n deviceBreakpoints: number[];\r\n explicitWidth?: number;\r\n sizesAttr?: string;\r\n}): { candidates: number[]; descriptor: 'w' | 'x' } {\r\n const {\r\n allBreakpoints,\r\n deviceBreakpoints,\r\n explicitWidth,\r\n sizesAttr\r\n } = params;\r\n\r\n // ---- Normalize & guard ----\r\n const sortedAll = [...new Set(allBreakpoints)].sort((a, b) => a - b);\r\n const sortedDevice = [...new Set(deviceBreakpoints)].sort((a, b) => a - b);\r\n\r\n if (!sortedAll.length || !sortedDevice.length) {\r\n return { candidates: [], descriptor: 'w' };\r\n }\r\n\r\n const minDevice = sortedDevice[0]!;\r\n const maxDevice = sortedDevice[sortedDevice.length - 1]!;\r\n\r\n const vwRatios = parseVW(sizesAttr);\r\n const hasVW = vwRatios.length > 0;\r\n\r\n // Explicit width (fixed layout) → x descriptors ----\r\n // Prefer this when layout width is known and sizes does NOT imply vw scaling\r\n if (typeof explicitWidth === 'number' && !hasVW) {\r\n const DPRs = [1, 2, 3];\r\n\r\n const candidates = Array.from(\r\n new Set(\r\n DPRs\r\n .map(dpr => nearest(sortedAll, explicitWidth * dpr))\r\n .map(clampMax)\r\n )\r\n ).filter(w => w <= MAX_IMAGE_WIDTH);\r\n\r\n return {\r\n candidates,\r\n descriptor: 'x'\r\n };\r\n }\r\n\r\n // Sizes with vw → w descriptors ----\r\n if (hasVW) {\r\n const minRatio = Math.min(...vwRatios);\r\n const maxRatio = Math.max(...vwRatios);\r\n\r\n const minPx = minDevice * minRatio;\r\n const maxPx = maxDevice * maxRatio * DEFAULT_MAX_DPR;\r\n\r\n const candidates = sortedAll.filter(\r\n w => w >= minPx && w <= clampMax(maxPx)\r\n );\r\n\r\n return {\r\n candidates,\r\n descriptor: 'w'\r\n };\r\n }\r\n\r\n // No sizes & no explicit width → device-based w ----\r\n const maxUseful = maxDevice * DEFAULT_MAX_DPR;\r\n\r\n return {\r\n candidates: sortedAll.filter(w => w <= clampMax(maxUseful)),\r\n descriptor: 'w'\r\n };\r\n}\r\n\r\n/**\r\n * Generates responsive image attributes (src, srcSet, sizes) for optimal loading.\r\n *\r\n * Supports two strategies:\r\n * 1. **Responsive (with `sizes`)**: Uses width descriptors (`w`) for fluid layouts\r\n * where image size varies with viewport.\r\n * 2. **Fixed width (with `width`)**: Uses DPR descriptors (`x`) for fixed-size\r\n * images that need high-DPI support.\r\n *\r\n * @param config - Configuration containing the base URL for the image\r\n * @param options - Options for responsive image generation\r\n * @returns Object containing src, srcSet, and optionally sizes attributes\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/photo.jpg' };\r\n *\r\n * // Responsive image with sizes attribute (uses `w` descriptors)\r\n * createResponsiveAttributes(config, { sizes: '(max-width: 768px) 100vw, 50vw' });\r\n * // => {\r\n * // src: '...?w=3840',\r\n * // srcSet: '...?w=640 640w, ...?w=750 750w, ..., ...?w=3840 3840w',\r\n * // sizes: '(max-width: 768px) 100vw, 50vw'\r\n * // }\r\n *\r\n * // Fixed-width image (uses `x` descriptors)\r\n * createResponsiveAttributes(config, { width: 400 });\r\n * // => {\r\n * // src: '...?w=384',\r\n * // srcSet: '...?w=384 1x, ...?w=828 2x',\r\n * // width: 400\r\n * // }\r\n *\r\n * // With transformations\r\n * createResponsiveAttributes(config, {\r\n * sizes: '100vw',\r\n * transformations: { format: 'WEBP', quality: 80 }\r\n * });\r\n * ```\r\n */\r\nexport const createResponsiveAttributes = (\r\n config: UrlBuilderConfig,\r\n options: ResponsiveImageOptions = {}\r\n): ResponsiveImageResult => {\r\n const {\r\n sizes,\r\n width,\r\n deviceBreakpoints = [...DEFAULT_DEVICE_BREAKPOINTS],\r\n imageBreakpoints = [...DEFAULT_IMAGE_BREAKPOINTS],\r\n transformations = {},\r\n mode = 'short',\r\n } = options;\r\n\r\n // If transformations.width is set, it takes precedence as the base width\r\n const baseWidth = transformations.width ?? width;\r\n\r\n const sortedDeviceBreakpoints = [...deviceBreakpoints].sort((a, b) => a - b);\r\n const sortedImageBreakpoints = [...imageBreakpoints].sort((a, b) => a - b);\r\n\r\n const allBreakpoints = [...new Set([...sortedImageBreakpoints, ...sortedDeviceBreakpoints])].sort((a, b) => a - b);\r\n\r\n const { candidates, descriptor } = computeCandidateWidths({\r\n allBreakpoints,\r\n deviceBreakpoints: sortedDeviceBreakpoints,\r\n ...(baseWidth !== undefined && { explicitWidth: baseWidth }),\r\n ...(sizes !== undefined && { sizesAttr: sizes }),\r\n });\r\n\r\n if (candidates.length === 0) {\r\n // Fallback: no valid candidates, return base URL\r\n return { src: config.baseUrl };\r\n }\r\n\r\n // Build srcSet entries\r\n const srcSetEntries = candidates.map((w, index,) => buildSrcEntries(w, index, config, transformations, mode, descriptor));\r\n\r\n // Default src is the largest candidate\r\n const largestWidth = candidates[candidates.length - 1]!;\r\n const src = buildTransformationUrl(\r\n config,\r\n { ...transformations, width: largestWidth },\r\n mode\r\n );\r\n\r\n const endSizes = sizes ?? (descriptor === 'w' ? '100vw' : undefined);\r\n\r\n return {\r\n src,\r\n ...(endSizes ? { sizes: endSizes } : {}),\r\n srcSet: srcSetEntries.join(', '),\r\n ...(width !== undefined ? { width } : {}),\r\n };\r\n};\r\n\r\nfunction buildSrcEntries(width: number, index: number, config: UrlBuilderConfig, transformations: TransformationOptions, mode: NameMode, descriptor: 'w' | 'x'): string {\r\n const url = buildTransformationUrl(\r\n config,\r\n { ...transformations, width: width },\r\n mode\r\n );\r\n\r\n const desc = descriptor === 'w' ? `${width}w` : `${index + 1}x`;\r\n\r\n return `${url} ${desc}`;\r\n}\r\n","import { Path, PathValue } from './util-types';\r\nimport { MAX_IMAGE_WIDTH } from './src-set';\r\n\r\nexport const normalizeHex = (value: string) => value.replace('#', '')\r\n\r\n\r\nexport const removeTrailingSlash = (baseUrl: string) => {\r\n return baseUrl.endsWith('/')\r\n ? baseUrl.slice(0, -1)\r\n : baseUrl;\r\n}\r\n\r\nexport const getNestedValue = <\r\n T,\r\n P extends Path<T>\r\n>(\r\n obj: T,\r\n path: P\r\n): PathValue<T, P> | undefined =>\r\n path.reduce<any>(\r\n (acc, key) => (acc == null ? undefined : acc[key]),\r\n obj\r\n );\r\n\r\n\r\nexport const clampMax = (v: number) => Math.min(v, MAX_IMAGE_WIDTH);\r\n\r\nexport const nearest = (sortedBreakpoints: number[],target: number) =>\r\n sortedBreakpoints.find(w => w >= target) ?? sortedBreakpoints[sortedBreakpoints.length - 1]!;\r\n\r\nexport const parseVW = (sizes?: string): number[] => {\r\n if (!sizes) return [];\r\n return [...sizes.matchAll(/(\\d+(?:\\.\\d+)?)vw/g)].map(\r\n m => Number(m[1]) / 100\r\n );\r\n};\r\n\r\n","import { ParamSpec, Path } from './util-types';\r\nimport { TransformationOptions } from './types';\r\nimport { normalizeHex } from './utils';\r\n\r\nexport const PARAMS = [\r\n {path: ['width'], long: 'width', short: 'w'},\r\n {path: ['height'], long: 'height', short: 'h'},\r\n {path: ['dpr'], long: 'dpr', short: 'dpr'},\r\n {path: ['mode'], long: 'mode', short: 'rm'},\r\n {\r\n path: ['background'],\r\n long: 'background',\r\n short: 'bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n\r\n {path: ['format'], long: 'format', short: 'f'},\r\n {path: ['quality'], long: 'quality', short: 'q'},\r\n {path: ['stripMetadata'], long: 'stripMetadata', short: 'st'},\r\n\r\n {path: ['blur'], long: 'blur', short: 'bl'},\r\n {path: ['brightness'], long: 'brightness', short: 'br'},\r\n {path: ['contrast'], long: 'contrast', short: 'ct'},\r\n {path: ['grayscale'], long: 'grayscale', short: 'gs'},\r\n {path: ['saturation'], long: 'saturation', short: 'sa'},\r\n {path: ['sepia'], long: 'sepia', short: 'sp'},\r\n {path: ['sharpen'], long: 'sharpen', short: 'sh'},\r\n {path: ['noise'], long: 'noise', short: 'ns'},\r\n {path: ['rotate'], long: 'rotate', short: 'rt'},\r\n\r\n {path: ['border', 'width'], long: 'border.width', short: 'bo.w'},\r\n {\r\n path: ['border', 'color'],\r\n long: 'border.color',\r\n short: 'bo.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['border', 'radius'], long: 'border.radius', short: 'bo.r'},\r\n\r\n {path: ['crop', 'type'], long: 'crop.type', short: 'cr.t'},\r\n {path: ['crop', 'objectType'], long: 'crop.objectType', short: 'cr.o'},\r\n {path: ['crop', 'focus', 'position', 'x'], long: 'crop.x', short: 'cr.x'},\r\n {path: ['crop', 'focus', 'position', 'y'], long: 'crop.y', short: 'cr.y'},\r\n {path: ['crop', 'focus', 'strategy'], long: 'crop', short: 'cr'},\r\n {path: ['crop', 'width'], long: 'crop.width', short: 'cr.w'},\r\n {path: ['crop', 'height'], long: 'crop.height', short: 'cr.h'},\r\n {path: ['crop', 'afterResize'], long: 'crop.afterResize', short: 'cr.ar'},\r\n\r\n {path: ['text', 'text'], long: 'text', short: 'tx'},\r\n {\r\n path: ['text', 'color'],\r\n long: 'text.color',\r\n short: 'tx.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'opacity'], long: 'text.opacity', short: 'tx.o'},\r\n {path: ['text', 'size'], long: 'text.size', short: 'tx.s'},\r\n {path: ['text', 'font'], long: 'text.font', short: 'tx.f'},\r\n {path: ['text', 'fontSize'], long: 'text.fontSize', short: 'tx.fs'},\r\n {path: ['text', 'position'], long: 'text.position', short: 'tx.p'},\r\n {\r\n path: ['text', 'background'],\r\n long: 'text.background',\r\n short: 'tx.bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'backgroundOpacity'], long: 'text.backgroundOpacity', short: 'tx.bg.o'},\r\n\r\n {path: ['watermark', 'name'], long: 'watermark', short: 'wm'},\r\n {path: ['watermark', 'opacity'], long: 'watermark.opacity', short: 'wm.o'},\r\n {path: ['watermark', 'size'], long: 'watermark.size', short: 'wm.s'},\r\n {path: ['watermark', 'width'], long: 'watermark.width', short: 'wm.w'},\r\n {path: ['watermark', 'height'], long: 'watermark.height', short: 'wm.h'},\r\n {path: ['watermark', 'position'], long: 'watermark.position', short: 'wm.p'},\r\n] as const satisfies readonly ParamSpec<\r\n TransformationOptions,\r\n Path<TransformationOptions>\r\n>[];\r\n","import { TransformationOptions, UrlBuilderConfig } from './types';\r\nimport { getNestedValue, removeTrailingSlash } from './utils';\r\nimport { PARAMS } from './config';\r\n\r\n/**\r\n * Determines which parameter names to use in the query string.\r\n * - `'short'` - Use abbreviated aliases (e.g., `w`, `h`, `q`)\r\n * - `'long'` - Use full parameter names (e.g., `width`, `height`, `quality`)\r\n */\r\nexport type NameMode = 'short' | 'long';\r\n\r\n/**\r\n * Converts transformation options into a URL-encoded query string.\r\n *\r\n * Iterates through all defined parameters in the PARAMS config, extracts values\r\n * from the options object using their paths, applies any normalization functions\r\n * (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.\r\n *\r\n * @param options - The transformation options object\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns URL-encoded query string (without leading `?`)\r\n *\r\n * @example\r\n * ```ts\r\n * // Using short aliases (default)\r\n * buildQueryParams({ width: 800, height: 600, quality: 80 });\r\n * // => 'w=800&h=600&q=80'\r\n *\r\n * // Using long parameter names\r\n * buildQueryParams({ width: 800, format: 'webp' }, 'long');\r\n * // => 'width=800&format=webp'\r\n *\r\n * // Nested options\r\n * buildQueryParams({ border: { width: 2, color: '#ff0000' } });\r\n * // => 'bo.w=2&bo.c=ff0000'\r\n * ```\r\n */\r\nexport const buildTransformationQueryParams = (\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n): string => {\r\n const params: Array<[string, string]> = [];\r\n\r\n for (const spec of PARAMS) {\r\n const value = getNestedValue(options, spec.path);\r\n if (value == null) continue;\r\n\r\n const name =\r\n mode === 'short' && spec.short ? spec.short : spec.long;\r\n\r\n let normalized = value\r\n if ('normalize' in spec) {\r\n normalized = spec.normalize(value)\r\n }\r\n\r\n params.push([name, String(normalized)]);\r\n }\r\n\r\n\r\n return new URLSearchParams(\r\n params.map(([k, v]) => [k, v])\r\n ).toString();\r\n\r\n};\r\n\r\n/**\r\n * Builds a complete transformation URL from a base URL and transformation options.\r\n *\r\n * Combines the configured base URL with query parameters generated from the\r\n * transformation options. Handles the special `original` flag which returns\r\n * the unmodified source image.\r\n *\r\n * @param config - Configuration containing the base URL for the image\r\n * @param options - The transformation options to apply\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns The complete URL string with query parameters\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/images/photo.jpg' };\r\n *\r\n * // Basic resize\r\n * buildTransformationUrl(config, { width: 400, height: 300 });\r\n * // => 'https://example.com/images/photo.jpg?w=400&h=300'\r\n *\r\n * // Multiple transformations\r\n * buildTransformationUrl(config, {\r\n * width: 800,\r\n * format: 'webp',\r\n * quality: 85,\r\n * blur: 5\r\n * });\r\n * // => 'https://example.com/images/photo.jpg?w=800&f=webp&q=85&bl=5'\r\n *\r\n * // Request original image (no transformations)\r\n * buildTransformationUrl(config, { original: true });\r\n * // => 'https://example.com/images/photo.jpg?original'\r\n *\r\n * // Using long parameter names\r\n * buildTransformationUrl(config, { width: 400 }, 'long');\r\n * // => 'https://example.com/images/photo.jpg?width=400'\r\n * ```\r\n */\r\n/**\r\n * Extracts the base URL path without query parameters\r\n */\r\nconst getBaseUrlWithoutQuery = (url: string): string => {\r\n const queryIndex = url.indexOf('?');\r\n return queryIndex === -1 ? url : url.slice(0, queryIndex);\r\n};\r\n\r\nexport const buildTransformationUrl = (\r\n config: UrlBuilderConfig,\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n) => {\r\n // Strip any existing query params from baseUrl - transformations replace them\r\n const baseUrl = removeTrailingSlash(getBaseUrlWithoutQuery(config.baseUrl));\r\n\r\n if (options.original) {\r\n return `${baseUrl}?original`;\r\n }\r\n\r\n const query = buildTransformationQueryParams(options, mode);\r\n\r\n return `${baseUrl}?${query}`;\r\n};\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/src-set.ts","../src/utils.ts","../src/config.ts","../src/url-builder.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { buildTransformationUrl, NameMode } from './url-builder';\r\nimport { TransformationOptions } from './types';\r\nimport { clampMax, getFinalUrl, nearest, parseVW } from './utils';\r\n\r\n/**\r\n * Maximum supported image width\r\n */\r\nexport const MAX_IMAGE_WIDTH = 4096;\r\n\r\n/**\r\n * Default device breakpoints for responsive srcset generation\r\n */\r\nexport const DEFAULT_DEVICE_BREAKPOINTS = [\r\n 320,\r\n 375,\r\n 640,\r\n 768,\r\n 1024,\r\n 1280,\r\n 1536,\r\n 1920,\r\n 2048,\r\n 3840\r\n] as const;\r\n\r\n/**\r\n * Default image breakpoints for smaller images (icons, thumbnails)\r\n */\r\nexport const DEFAULT_IMAGE_BREAKPOINTS = [16, 32, 48, 64, 96, 128, 256, 384] as const;\r\n\r\n/**\r\n * Options for responsive image attributes generation\r\n */\r\nexport interface ResponsiveImageOptions {\r\n /**\r\n * The `sizes` attribute value (e.g., \"100vw\", \"(max-width: 768px) 100vw, 50vw\").\r\n * When provided, generates width descriptors (`w`) for responsive layouts.\r\n */\r\n sizes?: string;\r\n /**\r\n * Fixed display width in CSS pixels.\r\n * When provided without `sizes`, generates DPR descriptors (`x`).\r\n */\r\n width?: number;\r\n /** Device breakpoints for viewport-based sizing (default: DEFAULT_DEVICE_BREAKPOINTS) */\r\n deviceBreakpoints?: number[];\r\n /** Image breakpoints for smaller images (default: DEFAULT_IMAGE_BREAKPOINTS) */\r\n imageBreakpoints?: number[];\r\n /** Base transformation options to apply to all variants */\r\n transformations?: TransformationOptions;\r\n /** Whether to use short or long parameter names (default: 'short') */\r\n mode?: NameMode;\r\n maxDpr?: number\r\n}\r\n\r\n/**\r\n * Result of responsive image attributes generation\r\n */\r\nexport interface ResponsiveImageResult {\r\n /** The src attribute value (largest/default variant) */\r\n src: string;\r\n /** The srcset attribute value */\r\n srcSet?: string;\r\n /** The sizes attribute value (for width descriptors) */\r\n sizes?: string;\r\n /** The width attribute value (when explicitly provided) */\r\n width?: number;\r\n}\r\n\r\n/**\r\n * Calculates candidate widths and descriptor type based on input options.\r\n */\r\nconst DEFAULT_MAX_DPR = 2;\r\n\r\nfunction calculatesCandidateWidths(params: {\r\n allBreakpoints: number[];\r\n deviceBreakpoints: number[];\r\n explicitWidth?: number;\r\n sizesAttr?: string;\r\n maxDpr: number\r\n}): { candidates: number[]; descriptor: 'w' | 'x' } {\r\n const {\r\n allBreakpoints,\r\n deviceBreakpoints,\r\n explicitWidth,\r\n sizesAttr,\r\n maxDpr\r\n } = params;\r\n\r\n if (!allBreakpoints.length || !deviceBreakpoints.length) {\r\n return {candidates: [], descriptor: 'w'};\r\n }\r\n\r\n const minDevice = deviceBreakpoints[0]!;\r\n const maxDevice = deviceBreakpoints[deviceBreakpoints.length - 1]!;\r\n\r\n const vwRatios = parseVW(sizesAttr);\r\n const hasVW = vwRatios.length > 0;\r\n\r\n\r\n // When no sizes with vw definition is provided go for x descriptor\r\n if (explicitWidth && !hasVW) {\r\n const DPRs = [1, 2, 3, 4].filter(item => item <= maxDpr);\r\n\r\n const candidates = Array.from(\r\n new Set(\r\n DPRs\r\n .map(dpr => nearest(allBreakpoints, explicitWidth * dpr))\r\n .map(clampMax)\r\n )\r\n ).filter(w => w <= MAX_IMAGE_WIDTH);\r\n\r\n return {\r\n candidates,\r\n descriptor: 'x'\r\n };\r\n }\r\n\r\n\r\n // Sizes with vw → w descriptors ----\r\n if (hasVW) {\r\n const lowestRatio = Math.min(...vwRatios) / 100;\r\n const minRequiredPx = minDevice * lowestRatio;\r\n\r\n\r\n return {\r\n // Responsive layout only uses device breakpoints\r\n candidates: deviceBreakpoints.filter(w => w >= minRequiredPx && w <= MAX_IMAGE_WIDTH),\r\n descriptor: 'w',\r\n };\r\n }\r\n\r\n // No sizes & no explicit width → device-based w ----\r\n const upperLimit = maxDevice * maxDpr;\r\n\r\n return {\r\n candidates: allBreakpoints.filter(w => w <= upperLimit),\r\n descriptor: 'w'\r\n };\r\n}\r\n\r\n/**\r\n * Generates responsive image attributes (src, srcSet, sizes) for optimal loading.\r\n *\r\n * Supports two strategies:\r\n * 1. **Responsive (with `sizes`)**: Uses width descriptors (`w`) for fluid layouts\r\n * where image size varies with viewport.\r\n * 2. **Fixed width (with `width`)**: Uses DPR descriptors (`x`) for fixed-size\r\n * images that need high-DPI support.\r\n *\r\n * @param imageSrc - Relative or an absolute path to the image\r\n * @param baseUrl - This should be your baseURL endpoint for the source from PixelFiddle Dashboard.\r\n * @param options - Options for responsive image generation\r\n * @returns Object containing src, srcSet, and optionally sizes attributes\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/photo.jpg' };\r\n *\r\n * // Responsive image with sizes attribute (uses `w` descriptors)\r\n * createResponsiveAttributes(config, { sizes: '(max-width: 768px) 100vw, 50vw' });\r\n * // => {\r\n * // src: '...?w=3840',\r\n * // srcSet: '...?w=640 640w, ...?w=750 750w, ..., ...?w=3840 3840w',\r\n * // sizes: '(max-width: 768px) 100vw, 50vw'\r\n * // }\r\n *\r\n * // Fixed-width image (uses `x` descriptors)\r\n * createResponsiveAttributes(config, { width: 400 });\r\n * // => {\r\n * // src: '...?w=384',\r\n * // srcSet: '...?w=384 1x, ...?w=828 2x',\r\n * // width: 400\r\n * // }\r\n *\r\n * // With transformations\r\n * createResponsiveAttributes(config, {\r\n * sizes: '100vw',\r\n * transformations: { format: 'WEBP', quality: 80 }\r\n * });\r\n * ```\r\n */\r\nexport const createResponsiveAttributes = (\r\n imageSrc: string,\r\n baseUrl?: string,\r\n options?: ResponsiveImageOptions\r\n): ResponsiveImageResult => {\r\n const {\r\n sizes,\r\n width,\r\n deviceBreakpoints = [...DEFAULT_DEVICE_BREAKPOINTS],\r\n imageBreakpoints = [...DEFAULT_IMAGE_BREAKPOINTS],\r\n transformations = {},\r\n mode = 'short',\r\n maxDpr = DEFAULT_MAX_DPR\r\n } = options || {}\r\n\r\n // If transformations.width is set, it takes precedence as the base width\r\n const baseWidth = transformations.width ?? width;\r\n\r\n const sortedDeviceBreakpoints = [...deviceBreakpoints].sort((a, b) => a - b);\r\n const sortedImageBreakpoints = [...imageBreakpoints].sort((a, b) => a - b);\r\n\r\n const allBreakpoints = [...new Set([...sortedImageBreakpoints, ...sortedDeviceBreakpoints])].sort((a, b) => a - b);\r\n\r\n const {candidates, descriptor} = calculatesCandidateWidths({\r\n allBreakpoints,\r\n deviceBreakpoints: sortedDeviceBreakpoints,\r\n ...(baseWidth !== undefined && {explicitWidth: baseWidth}),\r\n ...(sizes !== undefined && {sizesAttr: sizes}),\r\n maxDpr\r\n });\r\n\r\n if (candidates.length === 0) {\r\n // Fallback: no valid candidates, return base URL\r\n return {src: getFinalUrl(imageSrc, baseUrl)};\r\n }\r\n\r\n // Build srcSet entries\r\n const srcSetEntries = candidates.map((w, index,) => buildSrcEntries(imageSrc, w, index, transformations, mode, descriptor, baseUrl));\r\n\r\n // Default src is the largest candidate\r\n const largestWidth = candidates[candidates.length - 1]!;\r\n const src = buildTransformationUrl({\r\n src: imageSrc,\r\n baseUrl,\r\n transformations: {\r\n ...transformations,\r\n width: largestWidth\r\n },\r\n mode\r\n }\r\n );\r\n\r\n const endSizes = sizes ?? (descriptor === 'w' ? '100vw' : undefined);\r\n\r\n return {\r\n src,\r\n ...(endSizes ? {sizes: endSizes} : {}),\r\n srcSet: srcSetEntries.join(', '),\r\n ...(width !== undefined ? {width} : {}),\r\n };\r\n};\r\n\r\nfunction buildSrcEntries(imageSrc: string, width: number, index: number, transformations: TransformationOptions, mode: NameMode, descriptor: 'w' | 'x', baseUrl?: string,): string {\r\n const url = buildTransformationUrl({\r\n src: imageSrc,\r\n baseUrl,\r\n transformations: {...transformations, width: width},\r\n mode\r\n }\r\n );\r\n\r\n const desc = descriptor === 'w' ? `${width}w` : `${index + 1}x`;\r\n\r\n return `${url} ${desc}`;\r\n}\r\n","import { Path, PathValue } from './util-types';\r\nimport { MAX_IMAGE_WIDTH } from './src-set';\r\n\r\nexport const normalizeHex = (value: string) => value.replace('#', '')\r\n\r\n\r\nexport const removeTrailingSlash = (baseUrl: string) => {\r\n return baseUrl.endsWith('/')\r\n ? baseUrl.slice(0, -1)\r\n : baseUrl;\r\n}\r\n\r\nexport const getNestedValue = <\r\n T,\r\n P extends Path<T>\r\n>(\r\n obj: T,\r\n path: P\r\n): PathValue<T, P> | undefined =>\r\n path.reduce<any>(\r\n (acc, key) => (acc == null ? undefined : acc[key]),\r\n obj\r\n );\r\n\r\n\r\nexport const clampMax = (v: number) => Math.min(v, MAX_IMAGE_WIDTH);\r\n\r\nexport const nearest = (sortedBreakpoints: number[],target: number) =>\r\n sortedBreakpoints.find(w => w >= target) ?? sortedBreakpoints[sortedBreakpoints.length - 1]!;\r\n\r\nexport const parseVW = (sizes?: string): number[] => {\r\n if (!sizes) return [];\r\n const foundSizes = sizes.match(/(\\d+(?:\\.\\d+)?)vw/g) || []\r\n\r\n return foundSizes.map((size) => parseInt(size, 10))\r\n};\r\n\r\n/**\r\n * Extracts the base URL path without query parameters\r\n */\r\nconst getBaseUrlWithoutQuery = (url: string | undefined): string => {\r\n if (!url) return '';\r\n const queryIndex = url.indexOf('?');\r\n return queryIndex === -1 ? url : url.slice(0, queryIndex);\r\n};\r\n\r\n/**\r\n * Checks if a URL is absolute (starts with http:// or https://)\r\n */\r\nconst isAbsoluteUrl = (url: string | undefined): boolean => {\r\n if (!url) return false;\r\n return url.startsWith('http://') || url.startsWith('https://');\r\n};\r\n\r\n\r\nexport function getFinalUrl(src: string, baseUrl?: string): string {\r\n const cleanSrc = getBaseUrlWithoutQuery(src);\r\n\r\n if (isAbsoluteUrl(cleanSrc)) {\r\n return removeTrailingSlash(cleanSrc);\r\n } else {\r\n // Relative path - combine with baseUrl\r\n const base = removeTrailingSlash(getBaseUrlWithoutQuery(baseUrl));\r\n\r\n // Ensure path has leading slash for proper URL construction\r\n const normalizedPath = normalizePath(cleanSrc)\r\n return `${base}${normalizedPath}`;\r\n }\r\n\r\n}\r\n\r\nexport function normalizePath(src: string): string {\r\n return src && !src.startsWith('/') ? `/${src}` : src;\r\n}\r\n\r\n\r\nexport const parseNumberLikeParam = (\r\n width: number | `${number}`\r\n): number => {\r\n if (typeof width === 'number') {\r\n return width;\r\n }\r\n\r\n const parsed = Number(width);\r\n\r\n if (!Number.isFinite(parsed)) {\r\n throw new Error(`Invalid width value: ${width}`);\r\n }\r\n\r\n return parsed;\r\n};\r\n","import { ParamSpec, Path } from './util-types';\r\nimport { TransformationOptions } from './types';\r\nimport { normalizeHex } from './utils';\r\n\r\nexport const PARAMS = [\r\n {path: ['width'], long: 'width', short: 'w'},\r\n {path: ['height'], long: 'height', short: 'h'},\r\n {path: ['dpr'], long: 'dpr', short: 'dpr'},\r\n {path: ['mode'], long: 'mode', short: 'rm'},\r\n {\r\n path: ['background'],\r\n long: 'background',\r\n short: 'bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n\r\n {path: ['format'], long: 'format', short: 'f'},\r\n {path: ['quality'], long: 'quality', short: 'q'},\r\n {path: ['stripMetadata'], long: 'stripMetadata', short: 'st'},\r\n\r\n {path: ['blur'], long: 'blur', short: 'bl'},\r\n {path: ['brightness'], long: 'brightness', short: 'br'},\r\n {path: ['contrast'], long: 'contrast', short: 'ct'},\r\n {path: ['grayscale'], long: 'grayscale', short: 'gs'},\r\n {path: ['saturation'], long: 'saturation', short: 'sa'},\r\n {path: ['sepia'], long: 'sepia', short: 'sp'},\r\n {path: ['sharpen'], long: 'sharpen', short: 'sh'},\r\n {path: ['noise'], long: 'noise', short: 'ns'},\r\n {path: ['rotate'], long: 'rotate', short: 'rt'},\r\n\r\n {path: ['border', 'width'], long: 'border.width', short: 'bo.w'},\r\n {\r\n path: ['border', 'color'],\r\n long: 'border.color',\r\n short: 'bo.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['border', 'radius'], long: 'border.radius', short: 'bo.r'},\r\n\r\n {path: ['crop', 'type'], long: 'crop.type', short: 'cr.t'},\r\n {path: ['crop', 'objectType'], long: 'crop.objectType', short: 'cr.o'},\r\n {path: ['crop', 'focus', 'position', 'x'], long: 'crop.x', short: 'cr.x'},\r\n {path: ['crop', 'focus', 'position', 'y'], long: 'crop.y', short: 'cr.y'},\r\n {path: ['crop', 'focus', 'strategy'], long: 'crop', short: 'cr'},\r\n {path: ['crop', 'width'], long: 'crop.width', short: 'cr.w'},\r\n {path: ['crop', 'height'], long: 'crop.height', short: 'cr.h'},\r\n {path: ['crop', 'afterResize'], long: 'crop.afterResize', short: 'cr.ar'},\r\n\r\n {path: ['text', 'text'], long: 'text', short: 'tx'},\r\n {\r\n path: ['text', 'color'],\r\n long: 'text.color',\r\n short: 'tx.c',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'opacity'], long: 'text.opacity', short: 'tx.o'},\r\n {path: ['text', 'size'], long: 'text.size', short: 'tx.s'},\r\n {path: ['text', 'font'], long: 'text.font', short: 'tx.f'},\r\n {path: ['text', 'fontSize'], long: 'text.fontSize', short: 'tx.fs'},\r\n {path: ['text', 'position'], long: 'text.position', short: 'tx.p'},\r\n {\r\n path: ['text', 'background'],\r\n long: 'text.background',\r\n short: 'tx.bg',\r\n normalize: (v) => typeof v === 'string' ? normalizeHex(v) : v\r\n },\r\n {path: ['text', 'backgroundOpacity'], long: 'text.backgroundOpacity', short: 'tx.bg.o'},\r\n\r\n {path: ['watermark', 'name'], long: 'watermark', short: 'wm'},\r\n {path: ['watermark', 'opacity'], long: 'watermark.opacity', short: 'wm.o'},\r\n {path: ['watermark', 'size'], long: 'watermark.size', short: 'wm.s'},\r\n {path: ['watermark', 'width'], long: 'watermark.width', short: 'wm.w'},\r\n {path: ['watermark', 'height'], long: 'watermark.height', short: 'wm.h'},\r\n {path: ['watermark', 'position'], long: 'watermark.position', short: 'wm.p'},\r\n] as const satisfies readonly ParamSpec<\r\n TransformationOptions,\r\n Path<TransformationOptions>\r\n>[];\r\n","import { BuildTransformationConfig, TransformationOptions } from './types';\r\nimport { getFinalUrl, getNestedValue } from './utils';\r\nimport { PARAMS } from './config';\r\n\r\n/**\r\n * Determines which parameter names to use in the query string.\r\n * - `'short'` - Use abbreviated aliases (e.g., `w`, `h`, `q`)\r\n * - `'long'` - Use full parameter names (e.g., `width`, `height`, `quality`)\r\n */\r\nexport type NameMode = 'short' | 'long';\r\n\r\n/**\r\n * Converts transformation options into a URL-encoded query string.\r\n *\r\n * Iterates through all defined parameters in the PARAMS config, extracts values\r\n * from the options object using their paths, applies any normalization functions\r\n * (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.\r\n *\r\n * @param options - The transformation options object\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns URL-encoded query string (without leading `?`)\r\n *\r\n * @example\r\n * ```ts\r\n * // Using short aliases (default)\r\n * buildQueryParams({ width: 800, height: 600, quality: 80 });\r\n * // => 'w=800&h=600&q=80'\r\n *\r\n * // Using long parameter names\r\n * buildQueryParams({ width: 800, format: 'webp' }, 'long');\r\n * // => 'width=800&format=webp'\r\n *\r\n * // Nested options\r\n * buildQueryParams({ border: { width: 2, color: '#ff0000' } });\r\n * // => 'bo.w=2&bo.c=ff0000'\r\n * ```\r\n */\r\nexport const buildTransformationQueryParams = (\r\n options: TransformationOptions,\r\n mode: NameMode = 'short'\r\n): string => {\r\n const params: Array<[string, string]> = [];\r\n\r\n for (const spec of PARAMS) {\r\n const value = getNestedValue(options, spec.path);\r\n if (value == null) continue;\r\n\r\n const name =\r\n mode === 'short' && spec.short ? spec.short : spec.long;\r\n\r\n let normalized = value\r\n if ('normalize' in spec) {\r\n normalized = spec.normalize(value)\r\n }\r\n\r\n params.push([name, String(normalized)]);\r\n }\r\n\r\n\r\n return new URLSearchParams(\r\n params.map(([k, v]) => [k, v])\r\n ).toString();\r\n\r\n};\r\n\r\n/**\r\n * Builds a complete transformation URL from a base URL and transformation options.\r\n *\r\n * Combines the configured base URL with query parameters generated from the\r\n * transformation options. Handles the special `original` flag which returns\r\n * the unmodified source image.\r\n *\r\n * @param src - Absolute or relative path to your image\r\n * @param config - Configuration containing the base URL for the image\r\n * @param transformations - The transformation options to apply\r\n * @param mode - Whether to use short aliases or long parameter names (default: `'short'`)\r\n * @returns The complete URL string with query parameters\r\n *\r\n * @example\r\n * ```ts\r\n * const config = { baseUrl: 'https://example.com/images/photo.jpg' };\r\n *\r\n * // Basic resize\r\n * buildTransformationUrl(config, { width: 400, height: 300 });\r\n * // => 'https://example.com/images/photo.jpg?w=400&h=300'\r\n *\r\n * // Multiple transformations\r\n * buildTransformationUrl(config, {\r\n * width: 800,\r\n * format: 'webp',\r\n * quality: 85,\r\n * blur: 5\r\n * });\r\n * // => 'https://example.com/images/photo.jpg?w=800&f=webp&q=85&bl=5'\r\n *\r\n * // Request original image (no transformations)\r\n * buildTransformationUrl(config, { original: true });\r\n * // => 'https://example.com/images/photo.jpg?original'\r\n *\r\n * // Using long parameter names\r\n * buildTransformationUrl(config, { width: 400 }, 'long');\r\n * // => 'https://example.com/images/photo.jpg?width=400'\r\n * ```\r\n */\r\nexport const buildTransformationUrl = (\r\n {src, transformations = {}, baseUrl, mode = 'short'}: BuildTransformationConfig\r\n) => {\r\n const url = getFinalUrl(src, baseUrl)\r\n\r\n if (transformations.original) {\r\n return `${url}?original`;\r\n }\r\n\r\n const query = buildTransformationQueryParams(transformations, mode);\r\n\r\n return `${url}?${query}`;\r\n};\r\n"]}
|