@pixelfiddler/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +212 -0
- package/dist/index.cjs +132 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +304 -0
- package/dist/index.d.ts +304 -0
- package/dist/index.js +125 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PixelFiddler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# @pixelfiddler/core
|
|
2
|
+
|
|
3
|
+
Core utilities for building [PixelFiddler](https://pixel-fiddler.com) image transformation URLs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @pixelfiddler/core
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @pixelfiddler/core
|
|
11
|
+
# or
|
|
12
|
+
yarn add @pixelfiddler/core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { buildTransformationUrl, buildQueryParams } from '@pixelfiddler/core';
|
|
19
|
+
|
|
20
|
+
// Build a complete transformation URL
|
|
21
|
+
const url = buildTransformationUrl(
|
|
22
|
+
{ baseUrl: 'https://cdn.pixelfiddler.com/images/photo.jpg' },
|
|
23
|
+
{
|
|
24
|
+
width: 800,
|
|
25
|
+
height: 600,
|
|
26
|
+
format: 'webp',
|
|
27
|
+
quality: 85,
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
// => 'https://cdn.pixelfiddler.com/images/photo.jpg?w=800&h=600&f=webp&q=85'
|
|
31
|
+
|
|
32
|
+
// Or just get the query string
|
|
33
|
+
const params = buildQueryParams({
|
|
34
|
+
width: 400,
|
|
35
|
+
blur: 5,
|
|
36
|
+
grayscale: true,
|
|
37
|
+
});
|
|
38
|
+
// => 'w=400&bl=5&gs=true'
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## API
|
|
42
|
+
|
|
43
|
+
### `buildTransformationUrl(config, options, mode?)`
|
|
44
|
+
|
|
45
|
+
Builds a complete transformation URL.
|
|
46
|
+
|
|
47
|
+
| Parameter | Type | Description |
|
|
48
|
+
|-----------|------|-------------|
|
|
49
|
+
| `config` | `UrlBuilderConfig` | Configuration with `baseUrl` |
|
|
50
|
+
| `options` | `TransformationOptions` | Transformation options |
|
|
51
|
+
| `mode` | `'short' \| 'long'` | Parameter naming mode (default: `'short'`) |
|
|
52
|
+
|
|
53
|
+
### `buildQueryParams(options, mode?)`
|
|
54
|
+
|
|
55
|
+
Converts transformation options to a URL-encoded query string.
|
|
56
|
+
|
|
57
|
+
| Parameter | Type | Description |
|
|
58
|
+
|-----------|------|-------------|
|
|
59
|
+
| `options` | `TransformationOptions` | Transformation options |
|
|
60
|
+
| `mode` | `'short' \| 'long'` | Parameter naming mode (default: `'short'`) |
|
|
61
|
+
|
|
62
|
+
## Transformation Options
|
|
63
|
+
|
|
64
|
+
### Resize
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
{
|
|
68
|
+
width: 800, // Target width (px)
|
|
69
|
+
height: 600, // Target height (px)
|
|
70
|
+
dpr: 2, // Device pixel ratio (1-4)
|
|
71
|
+
mode: 'FIT', // 'FILL' | 'FIT' | 'PAD' | 'FORCE' | 'COVER'
|
|
72
|
+
background: '#ffffff' // Background color for PAD mode
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Format & Quality
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
{
|
|
80
|
+
format: 'webp', // 'jpeg' | 'png' | 'webp' | 'avif' | 'gif' | 'auto'
|
|
81
|
+
quality: 85, // 1-100
|
|
82
|
+
stripMetadata: true // Remove EXIF data
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Effects
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
{
|
|
90
|
+
blur: 5, // Gaussian blur (0-100)
|
|
91
|
+
brightness: 10, // Brightness adjustment (0-100)
|
|
92
|
+
contrast: 20, // Contrast adjustment (0-100)
|
|
93
|
+
saturation: 30, // Saturation adjustment (0-100)
|
|
94
|
+
sharpen: 15, // Sharpen intensity (0-100)
|
|
95
|
+
noise: 5, // Add noise (0-100)
|
|
96
|
+
grayscale: true, // Convert to grayscale
|
|
97
|
+
sepia: true, // Apply sepia effect
|
|
98
|
+
rotate: 90 // Rotation (0 | 90 | 180 | 270)
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Border
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
{
|
|
106
|
+
border: {
|
|
107
|
+
width: 2, // Border width (px)
|
|
108
|
+
color: '#000000', // Border color
|
|
109
|
+
radius: 8 // Corner radius (px)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Crop
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
{
|
|
118
|
+
crop: {
|
|
119
|
+
type: 'SIMPLE', // 'SIMPLE' | 'OBJECT'
|
|
120
|
+
objectType: 'FACE', // 'FACE' | 'UPPER_BODY' (for OBJECT type)
|
|
121
|
+
width: 400,
|
|
122
|
+
height: 300,
|
|
123
|
+
focus: {
|
|
124
|
+
type: 'AUTO', // 'SPECIFIED' | 'AUTO'
|
|
125
|
+
strategy: 'SMART', // 'CENTER' | 'SMART' | 'DETAIL'
|
|
126
|
+
position: { x: 50, y: 50 } // For SPECIFIED type
|
|
127
|
+
},
|
|
128
|
+
afterResize: true
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Text Overlay
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
{
|
|
137
|
+
text: {
|
|
138
|
+
text: 'Hello World',
|
|
139
|
+
color: '#ffffff',
|
|
140
|
+
opacity: 80,
|
|
141
|
+
size: 50,
|
|
142
|
+
font: 'Arial',
|
|
143
|
+
fontSize: 24,
|
|
144
|
+
position: 'CENTER',
|
|
145
|
+
background: '#000000',
|
|
146
|
+
backgroundOpacity: 50
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Watermark
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
{
|
|
155
|
+
watermark: {
|
|
156
|
+
name: 'logo', // Predefined watermark name
|
|
157
|
+
opacity: 50,
|
|
158
|
+
size: 25, // Size as percentage
|
|
159
|
+
width: 100, // Or explicit dimensions
|
|
160
|
+
height: 50,
|
|
161
|
+
position: 'BOTTOM_RIGHT'
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Special Options
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
{
|
|
170
|
+
original: true, // Return original image (no transformations)
|
|
171
|
+
alias: 'thumb' // Use predefined transformation alias
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Parameter Aliases
|
|
176
|
+
|
|
177
|
+
By default, short parameter names are used for smaller URLs:
|
|
178
|
+
|
|
179
|
+
| Option | Short | Long |
|
|
180
|
+
|--------|-------|------|
|
|
181
|
+
| width | `w` | `width` |
|
|
182
|
+
| height | `h` | `height` |
|
|
183
|
+
| format | `f` | `format` |
|
|
184
|
+
| quality | `q` | `quality` |
|
|
185
|
+
| blur | `bl` | `blur` |
|
|
186
|
+
| ... | ... | ... |
|
|
187
|
+
|
|
188
|
+
Use `mode: 'long'` for full parameter names:
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
buildQueryParams({ width: 800 }, 'long');
|
|
192
|
+
// => 'width=800'
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## TypeScript
|
|
196
|
+
|
|
197
|
+
Full TypeScript support with exported types:
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
import type {
|
|
201
|
+
TransformationOptions,
|
|
202
|
+
ResizeOptions,
|
|
203
|
+
CropOptions,
|
|
204
|
+
TextOptions,
|
|
205
|
+
WatermarkOptions,
|
|
206
|
+
UrlBuilderConfig,
|
|
207
|
+
} from '@pixelfiddler/core';
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/types.ts
|
|
4
|
+
var CROP_FOCUS = {
|
|
5
|
+
SPECIFIED: "SPECIFIED",
|
|
6
|
+
AUTO: "AUTO"
|
|
7
|
+
};
|
|
8
|
+
var CROP_FOCUS_STRATEGY = {
|
|
9
|
+
CENTER: "CENTER",
|
|
10
|
+
SMART: "SMART",
|
|
11
|
+
DETAIL: "DETAIL"
|
|
12
|
+
};
|
|
13
|
+
var CROP_TYPE = {
|
|
14
|
+
SIMPLE: "SIMPLE",
|
|
15
|
+
OBJECT: "OBJECT"
|
|
16
|
+
};
|
|
17
|
+
var CROP_OBJECT_TYPE = {
|
|
18
|
+
FACE: "FACE",
|
|
19
|
+
UPPER_BODY: "UPPER_BODY"
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// src/utils.ts
|
|
23
|
+
var normalizeHex = (value) => value.replace("#", "");
|
|
24
|
+
var removeTrailingSlash = (baseUrl) => {
|
|
25
|
+
return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
26
|
+
};
|
|
27
|
+
var getNestedValue = (obj, path) => path.reduce(
|
|
28
|
+
(acc, key) => acc == null ? void 0 : acc[key],
|
|
29
|
+
obj
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// src/config.ts
|
|
33
|
+
var PARAMS = [
|
|
34
|
+
{ path: ["width"], long: "width", short: "w" },
|
|
35
|
+
{ path: ["height"], long: "height", short: "h" },
|
|
36
|
+
{ path: ["dpr"], long: "dpr", short: "dpr" },
|
|
37
|
+
{ path: ["mode"], long: "mode", short: "rm" },
|
|
38
|
+
{
|
|
39
|
+
path: ["background"],
|
|
40
|
+
long: "background",
|
|
41
|
+
short: "bg",
|
|
42
|
+
normalize: (v) => typeof v === "string" ? normalizeHex(v) : v
|
|
43
|
+
},
|
|
44
|
+
{ path: ["format"], long: "format", short: "f" },
|
|
45
|
+
{ path: ["quality"], long: "quality", short: "q" },
|
|
46
|
+
{ path: ["stripMetadata"], long: "stripMetadata", short: "st" },
|
|
47
|
+
{ path: ["blur"], long: "blur", short: "bl" },
|
|
48
|
+
{ path: ["brightness"], long: "brightness", short: "br" },
|
|
49
|
+
{ path: ["contrast"], long: "contrast", short: "ct" },
|
|
50
|
+
{ path: ["grayscale"], long: "grayscale", short: "gs" },
|
|
51
|
+
{ path: ["saturation"], long: "saturation", short: "sa" },
|
|
52
|
+
{ path: ["sepia"], long: "sepia", short: "sp" },
|
|
53
|
+
{ path: ["sharpen"], long: "sharpen", short: "sh" },
|
|
54
|
+
{ path: ["noise"], long: "noise", short: "ns" },
|
|
55
|
+
{ path: ["rotate"], long: "rotate", short: "rt" },
|
|
56
|
+
{ path: ["border", "width"], long: "border.width", short: "bo.w" },
|
|
57
|
+
{
|
|
58
|
+
path: ["border", "color"],
|
|
59
|
+
long: "border.color",
|
|
60
|
+
short: "bo.c",
|
|
61
|
+
normalize: (v) => typeof v === "string" ? normalizeHex(v) : v
|
|
62
|
+
},
|
|
63
|
+
{ path: ["border", "radius"], long: "border.radius", short: "bo.r" },
|
|
64
|
+
{ path: ["crop", "type"], long: "crop.type", short: "cr.t" },
|
|
65
|
+
{ path: ["crop", "objectType"], long: "crop.objectType", short: "cr.o" },
|
|
66
|
+
{ path: ["crop", "focus", "position", "x"], long: "crop.x", short: "cr.x" },
|
|
67
|
+
{ path: ["crop", "focus", "position", "y"], long: "crop.y", short: "cr.y" },
|
|
68
|
+
{ path: ["crop", "focus", "strategy"], long: "crop", short: "cr" },
|
|
69
|
+
{ path: ["crop", "width"], long: "crop.width", short: "cr.w" },
|
|
70
|
+
{ path: ["crop", "height"], long: "crop.height", short: "cr.h" },
|
|
71
|
+
{ path: ["crop", "afterResize"], long: "crop.afterResize", short: "cr.ar" },
|
|
72
|
+
{ path: ["text", "text"], long: "text", short: "tx" },
|
|
73
|
+
{
|
|
74
|
+
path: ["text", "color"],
|
|
75
|
+
long: "text.color",
|
|
76
|
+
short: "tx.c",
|
|
77
|
+
normalize: (v) => typeof v === "string" ? normalizeHex(v) : v
|
|
78
|
+
},
|
|
79
|
+
{ path: ["text", "opacity"], long: "text.opacity", short: "tx.o" },
|
|
80
|
+
{ path: ["text", "size"], long: "text.size", short: "tx.s" },
|
|
81
|
+
{ path: ["text", "font"], long: "text.font", short: "tx.f" },
|
|
82
|
+
{ path: ["text", "fontSize"], long: "text.fontSize", short: "tx.fs" },
|
|
83
|
+
{ path: ["text", "position"], long: "text.position", short: "tx.p" },
|
|
84
|
+
{
|
|
85
|
+
path: ["text", "background"],
|
|
86
|
+
long: "text.background",
|
|
87
|
+
short: "tx.bg",
|
|
88
|
+
normalize: (v) => typeof v === "string" ? normalizeHex(v) : v
|
|
89
|
+
},
|
|
90
|
+
{ path: ["text", "backgroundOpacity"], long: "text.backgroundOpacity", short: "tx.bg.o" },
|
|
91
|
+
{ path: ["watermark", "name"], long: "watermark", short: "wm" },
|
|
92
|
+
{ path: ["watermark", "opacity"], long: "watermark.opacity", short: "wm.o" },
|
|
93
|
+
{ path: ["watermark", "size"], long: "watermark.size", short: "wm.s" },
|
|
94
|
+
{ path: ["watermark", "width"], long: "watermark.width", short: "wm.w" },
|
|
95
|
+
{ path: ["watermark", "height"], long: "watermark.height", short: "wm.h" },
|
|
96
|
+
{ path: ["watermark", "position"], long: "watermark.position", short: "wm.p" }
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
// src/url-builder.ts
|
|
100
|
+
var buildQueryParams = (options, mode = "short") => {
|
|
101
|
+
const params = [];
|
|
102
|
+
for (const spec of PARAMS) {
|
|
103
|
+
const value = getNestedValue(options, spec.path);
|
|
104
|
+
if (value == null) continue;
|
|
105
|
+
const name = mode === "short" && spec.short ? spec.short : spec.long;
|
|
106
|
+
let normalized = value;
|
|
107
|
+
if ("normalize" in spec) {
|
|
108
|
+
normalized = spec.normalize(value);
|
|
109
|
+
}
|
|
110
|
+
params.push([name, String(normalized)]);
|
|
111
|
+
}
|
|
112
|
+
return new URLSearchParams(
|
|
113
|
+
params.map(([k, v]) => [k, v])
|
|
114
|
+
).toString();
|
|
115
|
+
};
|
|
116
|
+
var buildTransformationUrl = (config, options, mode = "short") => {
|
|
117
|
+
if (options.original) {
|
|
118
|
+
return `${config.baseUrl}?original`;
|
|
119
|
+
}
|
|
120
|
+
const query = buildQueryParams(options, mode);
|
|
121
|
+
const baseUrl = removeTrailingSlash(config.baseUrl);
|
|
122
|
+
return `${baseUrl}?${query}`;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
exports.CROP_FOCUS = CROP_FOCUS;
|
|
126
|
+
exports.CROP_FOCUS_STRATEGY = CROP_FOCUS_STRATEGY;
|
|
127
|
+
exports.CROP_OBJECT_TYPE = CROP_OBJECT_TYPE;
|
|
128
|
+
exports.CROP_TYPE = CROP_TYPE;
|
|
129
|
+
exports.buildQueryParams = buildQueryParams;
|
|
130
|
+
exports.buildTransformationUrl = buildTransformationUrl;
|
|
131
|
+
//# sourceMappingURL=index.cjs.map
|
|
132
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/utils.ts","../src/config.ts","../src/url-builder.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * Helper type to extract values from const objects\r\n */\r\ntype ObjectValue<T> = T[keyof T];\r\n\r\n/**\r\n * Resize mode for image transformations\r\n */\r\nexport type ResizeMode = 'FILL' | 'FIT' | 'PAD' | 'FORCE' | 'COVER';\r\n\r\n/**\r\n * Output image format - null or empty means AUTO\r\n */\r\nexport type ImageFormat = 'jpeg' | 'png' | 'webp' | 'avif' | 'gif'\r\n\r\n/**\r\n * Position for text overlays and watermarks\r\n */\r\nexport type Position =\r\n | 'TOP_LEFT'\r\n | 'TOP_CENTER'\r\n | 'TOP_RIGHT'\r\n | 'CENTER_LEFT'\r\n | 'CENTER'\r\n | 'CENTER_RIGHT'\r\n | 'BOTTOM_LEFT'\r\n | 'BOTTOM_CENTER'\r\n | 'BOTTOM_RIGHT';\r\n\r\n/**\r\n * Crop focus type\r\n */\r\nexport const CROP_FOCUS = {\r\n SPECIFIED: 'SPECIFIED',\r\n AUTO: 'AUTO',\r\n} as const;\r\n\r\nexport type CropFocus = ObjectValue<typeof CROP_FOCUS>;\r\n\r\n/**\r\n * Crop focus strategy for AUTO focus\r\n */\r\nexport const CROP_FOCUS_STRATEGY = {\r\n CENTER: 'CENTER',\r\n SMART: 'SMART',\r\n DETAIL: 'DETAIL',\r\n} as const;\r\n\r\nexport type CropFocusStrategy = ObjectValue<typeof CROP_FOCUS_STRATEGY>;\r\n\r\n/**\r\n * Crop type\r\n */\r\nexport const CROP_TYPE = {\r\n SIMPLE: 'SIMPLE',\r\n OBJECT: 'OBJECT',\r\n} as const;\r\n\r\nexport type CropType = ObjectValue<typeof CROP_TYPE>;\r\n\r\n/**\r\n * Object type for smart cropping\r\n */\r\nexport const CROP_OBJECT_TYPE = {\r\n FACE: 'FACE',\r\n UPPER_BODY: 'UPPER_BODY',\r\n} as const;\r\n\r\nexport type CropObjectType = ObjectValue<typeof CROP_OBJECT_TYPE>;\r\n\r\n/**\r\n * Rotation angle in degrees (clockwise)\r\n */\r\nexport type RotationAngle = 0 | 90 | 180 | 270;\r\n\r\n/**\r\n * Device pixel ratio for high-density displays\r\n */\r\nexport type DevicePixelRatio = 1 | 2 | 3 | 4;\r\n\r\n/**\r\n * Hex color string (with or without #)\r\n */\r\nexport type HexColor = string;\r\n\r\n/**\r\n * Resize transformation options\r\n */\r\nexport interface ResizeOptions {\r\n /** Target width in pixels (1-4096) */\r\n width?: number;\r\n /** Target height in pixels (1-4096) */\r\n height?: number;\r\n /** Device pixel ratio for retina displays */\r\n dpr?: DevicePixelRatio;\r\n /** Resize strategy */\r\n mode?: ResizeMode;\r\n /** Background color for PAD mode (hex) */\r\n background?: HexColor;\r\n}\r\n\r\n/**\r\n * Format and quality options\r\n */\r\nexport interface FormatOptions {\r\n /** Output format */\r\n format?: ImageFormat;\r\n /** Quality level (1-100) */\r\n quality?: number;\r\n /** Strip metadata from output */\r\n stripMetadata?: boolean;\r\n}\r\n\r\n/**\r\n * Effect transformation options\r\n */\r\nexport interface EffectOptions {\r\n /** Gaussian blur intensity (0-100) */\r\n blur?: number;\r\n /** Brightness adjustment (0-100) */\r\n brightness?: number;\r\n /** Contrast adjustment (0-100) */\r\n contrast?: number;\r\n /** Convert to grayscale */\r\n grayscale?: boolean;\r\n /** Saturation adjustment (0-100) */\r\n saturation?: number;\r\n /** Apply sepia effect */\r\n sepia?: boolean;\r\n /** Sharpen intensity (0-100) */\r\n sharpen?: number;\r\n /** Add noise intensity (0-100) */\r\n noise?: number;\r\n /** Rotation angle */\r\n rotate?: RotationAngle;\r\n}\r\n\r\n/**\r\n * Border options\r\n */\r\nexport interface BorderOptions {\r\n /** Border width in pixels */\r\n width?: number;\r\n /** Border color (hex) */\r\n color?: HexColor;\r\n /** Corner radius in pixels (1-2000) */\r\n radius?: number;\r\n}\r\n\r\n/**\r\n * Crop focus position for SPECIFIED focus type\r\n */\r\nexport interface CropFocusPosition {\r\n x?: number;\r\n y?: number;\r\n}\r\n\r\n/**\r\n * Crop focus options\r\n */\r\nexport interface CropFocusOptions {\r\n /** Focus type - SPECIFIED (manual position) or AUTO (strategy-based) */\r\n type?: CropFocus;\r\n /** Manual focus position when type is SPECIFIED */\r\n position?: CropFocusPosition;\r\n /** Auto focus strategy when type is AUTO */\r\n strategy?: CropFocusStrategy;\r\n}\r\n\r\n/**\r\n * Crop options\r\n */\r\nexport interface CropOptions {\r\n /** Crop type - SIMPLE or OBJECT */\r\n type?: CropType;\r\n /** Object type for OBJECT crop type */\r\n objectType?: CropObjectType;\r\n /** Focus options */\r\n focus?: CropFocusOptions;\r\n /** Crop width in pixels */\r\n width?: number;\r\n /** Crop height in pixels */\r\n height?: number;\r\n /** Apply crop after resize */\r\n afterResize?: boolean;\r\n}\r\n\r\n/**\r\n * Text overlay options\r\n */\r\nexport interface TextOptions {\r\n /** Text content to render */\r\n text?: string;\r\n /** Text color (hex) */\r\n color?: HexColor;\r\n /** Text opacity (0-100) */\r\n opacity?: number;\r\n /** Background color behind text (hex) */\r\n background?: HexColor;\r\n /** Background opacity (0-100) */\r\n backgroundOpacity?: number;\r\n /** Text size as percentage of image (1-100) */\r\n size?: number;\r\n /** Font family name */\r\n font?: string;\r\n /** Font size in pixels */\r\n fontSize?: number;\r\n /** Text position */\r\n position?: Position;\r\n}\r\n\r\n/**\r\n * Watermark options\r\n */\r\nexport interface WatermarkOptions {\r\n /** Watermark name (predefined in dashboard) */\r\n name?: string;\r\n /** Watermark opacity (0-100) */\r\n opacity?: number;\r\n /** Watermark size as percentage (1-100) */\r\n size?: number;\r\n /** Watermark width in pixels */\r\n width?: number;\r\n /** Watermark height in pixels */\r\n height?: number;\r\n /** Watermark position */\r\n position?: Position;\r\n}\r\n\r\n/**\r\n * All transformation options combined\r\n */\r\nexport interface TransformationOptions\r\n extends ResizeOptions,\r\n FormatOptions,\r\n EffectOptions {\r\n /** Border options */\r\n border?: BorderOptions;\r\n /** Crop options */\r\n crop?: CropOptions;\r\n /** Text overlay options */\r\n text?: TextOptions;\r\n /** Watermark options */\r\n watermark?: WatermarkOptions;\r\n /** Use predefined transformation alias */\r\n alias?: string;\r\n /** Return original image without transformations */\r\n original?: boolean;\r\n}\r\n\r\n/**\r\n * Configuration for PixelFiddler client\r\n */\r\nexport interface PixelFiddlerConfig {\r\n /** Base URL for the CDN (defaults to https://cdn.pixel-fiddler.com) */\r\n baseUrl?: string;\r\n /** Secret key for URL signing */\r\n signatureKey?: string;\r\n}\r\n\r\n/**\r\n * URL builder configuration (internal)\r\n */\r\nexport interface UrlBuilderConfig extends PixelFiddlerConfig {\r\n /** Resolved base URL */\r\n baseUrl: string;\r\n}\r\n\r\n/**\r\n * Mapping of transformation options to query parameter names\r\n */\r\nexport interface QueryParameterMapping {\r\n /** Full parameter name */\r\n name: string;\r\n /** Short alias */\r\n alias?: string;\r\n}\r\n\r\n\r\n","import { Path, PathValue } from './util-types';\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","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\ntype 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 buildQueryParams = (\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://cdn.example.com/images/photo.jpg' };\r\n *\r\n * // Basic resize\r\n * buildTransformationUrl(config, { width: 400, height: 300 });\r\n * // => 'https://cdn.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://cdn.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://cdn.example.com/images/photo.jpg?original'\r\n *\r\n * // Using long parameter names\r\n * buildTransformationUrl(config, { width: 400 }, 'long');\r\n * // => 'https://cdn.example.com/images/photo.jpg?width=400'\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 if (options.original) {\r\n return `${config.baseUrl}?original`;\r\n }\r\n\r\n const query = buildQueryParams(options, mode);\r\n\r\n const baseUrl = removeTrailingSlash(config.baseUrl)\r\n return `${baseUrl}?${query}`;\r\n};\r\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper type to extract values from const objects
|
|
3
|
+
*/
|
|
4
|
+
type ObjectValue<T> = T[keyof T];
|
|
5
|
+
/**
|
|
6
|
+
* Resize mode for image transformations
|
|
7
|
+
*/
|
|
8
|
+
type ResizeMode = 'FILL' | 'FIT' | 'PAD' | 'FORCE' | 'COVER';
|
|
9
|
+
/**
|
|
10
|
+
* Output image format - null or empty means AUTO
|
|
11
|
+
*/
|
|
12
|
+
type ImageFormat = 'jpeg' | 'png' | 'webp' | 'avif' | 'gif';
|
|
13
|
+
/**
|
|
14
|
+
* Position for text overlays and watermarks
|
|
15
|
+
*/
|
|
16
|
+
type Position = 'TOP_LEFT' | 'TOP_CENTER' | 'TOP_RIGHT' | 'CENTER_LEFT' | 'CENTER' | 'CENTER_RIGHT' | 'BOTTOM_LEFT' | 'BOTTOM_CENTER' | 'BOTTOM_RIGHT';
|
|
17
|
+
/**
|
|
18
|
+
* Crop focus type
|
|
19
|
+
*/
|
|
20
|
+
declare const CROP_FOCUS: {
|
|
21
|
+
readonly SPECIFIED: "SPECIFIED";
|
|
22
|
+
readonly AUTO: "AUTO";
|
|
23
|
+
};
|
|
24
|
+
type CropFocus = ObjectValue<typeof CROP_FOCUS>;
|
|
25
|
+
/**
|
|
26
|
+
* Crop focus strategy for AUTO focus
|
|
27
|
+
*/
|
|
28
|
+
declare const CROP_FOCUS_STRATEGY: {
|
|
29
|
+
readonly CENTER: "CENTER";
|
|
30
|
+
readonly SMART: "SMART";
|
|
31
|
+
readonly DETAIL: "DETAIL";
|
|
32
|
+
};
|
|
33
|
+
type CropFocusStrategy = ObjectValue<typeof CROP_FOCUS_STRATEGY>;
|
|
34
|
+
/**
|
|
35
|
+
* Crop type
|
|
36
|
+
*/
|
|
37
|
+
declare const CROP_TYPE: {
|
|
38
|
+
readonly SIMPLE: "SIMPLE";
|
|
39
|
+
readonly OBJECT: "OBJECT";
|
|
40
|
+
};
|
|
41
|
+
type CropType = ObjectValue<typeof CROP_TYPE>;
|
|
42
|
+
/**
|
|
43
|
+
* Object type for smart cropping
|
|
44
|
+
*/
|
|
45
|
+
declare const CROP_OBJECT_TYPE: {
|
|
46
|
+
readonly FACE: "FACE";
|
|
47
|
+
readonly UPPER_BODY: "UPPER_BODY";
|
|
48
|
+
};
|
|
49
|
+
type CropObjectType = ObjectValue<typeof CROP_OBJECT_TYPE>;
|
|
50
|
+
/**
|
|
51
|
+
* Rotation angle in degrees (clockwise)
|
|
52
|
+
*/
|
|
53
|
+
type RotationAngle = 0 | 90 | 180 | 270;
|
|
54
|
+
/**
|
|
55
|
+
* Device pixel ratio for high-density displays
|
|
56
|
+
*/
|
|
57
|
+
type DevicePixelRatio = 1 | 2 | 3 | 4;
|
|
58
|
+
/**
|
|
59
|
+
* Hex color string (with or without #)
|
|
60
|
+
*/
|
|
61
|
+
type HexColor = string;
|
|
62
|
+
/**
|
|
63
|
+
* Resize transformation options
|
|
64
|
+
*/
|
|
65
|
+
interface ResizeOptions {
|
|
66
|
+
/** Target width in pixels (1-4096) */
|
|
67
|
+
width?: number;
|
|
68
|
+
/** Target height in pixels (1-4096) */
|
|
69
|
+
height?: number;
|
|
70
|
+
/** Device pixel ratio for retina displays */
|
|
71
|
+
dpr?: DevicePixelRatio;
|
|
72
|
+
/** Resize strategy */
|
|
73
|
+
mode?: ResizeMode;
|
|
74
|
+
/** Background color for PAD mode (hex) */
|
|
75
|
+
background?: HexColor;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Format and quality options
|
|
79
|
+
*/
|
|
80
|
+
interface FormatOptions {
|
|
81
|
+
/** Output format */
|
|
82
|
+
format?: ImageFormat;
|
|
83
|
+
/** Quality level (1-100) */
|
|
84
|
+
quality?: number;
|
|
85
|
+
/** Strip metadata from output */
|
|
86
|
+
stripMetadata?: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Effect transformation options
|
|
90
|
+
*/
|
|
91
|
+
interface EffectOptions {
|
|
92
|
+
/** Gaussian blur intensity (0-100) */
|
|
93
|
+
blur?: number;
|
|
94
|
+
/** Brightness adjustment (0-100) */
|
|
95
|
+
brightness?: number;
|
|
96
|
+
/** Contrast adjustment (0-100) */
|
|
97
|
+
contrast?: number;
|
|
98
|
+
/** Convert to grayscale */
|
|
99
|
+
grayscale?: boolean;
|
|
100
|
+
/** Saturation adjustment (0-100) */
|
|
101
|
+
saturation?: number;
|
|
102
|
+
/** Apply sepia effect */
|
|
103
|
+
sepia?: boolean;
|
|
104
|
+
/** Sharpen intensity (0-100) */
|
|
105
|
+
sharpen?: number;
|
|
106
|
+
/** Add noise intensity (0-100) */
|
|
107
|
+
noise?: number;
|
|
108
|
+
/** Rotation angle */
|
|
109
|
+
rotate?: RotationAngle;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Border options
|
|
113
|
+
*/
|
|
114
|
+
interface BorderOptions {
|
|
115
|
+
/** Border width in pixels */
|
|
116
|
+
width?: number;
|
|
117
|
+
/** Border color (hex) */
|
|
118
|
+
color?: HexColor;
|
|
119
|
+
/** Corner radius in pixels (1-2000) */
|
|
120
|
+
radius?: number;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Crop focus position for SPECIFIED focus type
|
|
124
|
+
*/
|
|
125
|
+
interface CropFocusPosition {
|
|
126
|
+
x?: number;
|
|
127
|
+
y?: number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Crop focus options
|
|
131
|
+
*/
|
|
132
|
+
interface CropFocusOptions {
|
|
133
|
+
/** Focus type - SPECIFIED (manual position) or AUTO (strategy-based) */
|
|
134
|
+
type?: CropFocus;
|
|
135
|
+
/** Manual focus position when type is SPECIFIED */
|
|
136
|
+
position?: CropFocusPosition;
|
|
137
|
+
/** Auto focus strategy when type is AUTO */
|
|
138
|
+
strategy?: CropFocusStrategy;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Crop options
|
|
142
|
+
*/
|
|
143
|
+
interface CropOptions {
|
|
144
|
+
/** Crop type - SIMPLE or OBJECT */
|
|
145
|
+
type?: CropType;
|
|
146
|
+
/** Object type for OBJECT crop type */
|
|
147
|
+
objectType?: CropObjectType;
|
|
148
|
+
/** Focus options */
|
|
149
|
+
focus?: CropFocusOptions;
|
|
150
|
+
/** Crop width in pixels */
|
|
151
|
+
width?: number;
|
|
152
|
+
/** Crop height in pixels */
|
|
153
|
+
height?: number;
|
|
154
|
+
/** Apply crop after resize */
|
|
155
|
+
afterResize?: boolean;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Text overlay options
|
|
159
|
+
*/
|
|
160
|
+
interface TextOptions {
|
|
161
|
+
/** Text content to render */
|
|
162
|
+
text?: string;
|
|
163
|
+
/** Text color (hex) */
|
|
164
|
+
color?: HexColor;
|
|
165
|
+
/** Text opacity (0-100) */
|
|
166
|
+
opacity?: number;
|
|
167
|
+
/** Background color behind text (hex) */
|
|
168
|
+
background?: HexColor;
|
|
169
|
+
/** Background opacity (0-100) */
|
|
170
|
+
backgroundOpacity?: number;
|
|
171
|
+
/** Text size as percentage of image (1-100) */
|
|
172
|
+
size?: number;
|
|
173
|
+
/** Font family name */
|
|
174
|
+
font?: string;
|
|
175
|
+
/** Font size in pixels */
|
|
176
|
+
fontSize?: number;
|
|
177
|
+
/** Text position */
|
|
178
|
+
position?: Position;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Watermark options
|
|
182
|
+
*/
|
|
183
|
+
interface WatermarkOptions {
|
|
184
|
+
/** Watermark name (predefined in dashboard) */
|
|
185
|
+
name?: string;
|
|
186
|
+
/** Watermark opacity (0-100) */
|
|
187
|
+
opacity?: number;
|
|
188
|
+
/** Watermark size as percentage (1-100) */
|
|
189
|
+
size?: number;
|
|
190
|
+
/** Watermark width in pixels */
|
|
191
|
+
width?: number;
|
|
192
|
+
/** Watermark height in pixels */
|
|
193
|
+
height?: number;
|
|
194
|
+
/** Watermark position */
|
|
195
|
+
position?: Position;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* All transformation options combined
|
|
199
|
+
*/
|
|
200
|
+
interface TransformationOptions extends ResizeOptions, FormatOptions, EffectOptions {
|
|
201
|
+
/** Border options */
|
|
202
|
+
border?: BorderOptions;
|
|
203
|
+
/** Crop options */
|
|
204
|
+
crop?: CropOptions;
|
|
205
|
+
/** Text overlay options */
|
|
206
|
+
text?: TextOptions;
|
|
207
|
+
/** Watermark options */
|
|
208
|
+
watermark?: WatermarkOptions;
|
|
209
|
+
/** Use predefined transformation alias */
|
|
210
|
+
alias?: string;
|
|
211
|
+
/** Return original image without transformations */
|
|
212
|
+
original?: boolean;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Configuration for PixelFiddler client
|
|
216
|
+
*/
|
|
217
|
+
interface PixelFiddlerConfig {
|
|
218
|
+
/** Base URL for the CDN (defaults to https://cdn.pixel-fiddler.com) */
|
|
219
|
+
baseUrl?: string;
|
|
220
|
+
/** Secret key for URL signing */
|
|
221
|
+
signatureKey?: string;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* URL builder configuration (internal)
|
|
225
|
+
*/
|
|
226
|
+
interface UrlBuilderConfig extends PixelFiddlerConfig {
|
|
227
|
+
/** Resolved base URL */
|
|
228
|
+
baseUrl: string;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Determines which parameter names to use in the query string.
|
|
233
|
+
* - `'short'` - Use abbreviated aliases (e.g., `w`, `h`, `q`)
|
|
234
|
+
* - `'long'` - Use full parameter names (e.g., `width`, `height`, `quality`)
|
|
235
|
+
*/
|
|
236
|
+
type NameMode = 'short' | 'long';
|
|
237
|
+
/**
|
|
238
|
+
* Converts transformation options into a URL-encoded query string.
|
|
239
|
+
*
|
|
240
|
+
* Iterates through all defined parameters in the PARAMS config, extracts values
|
|
241
|
+
* from the options object using their paths, applies any normalization functions
|
|
242
|
+
* (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.
|
|
243
|
+
*
|
|
244
|
+
* @param options - The transformation options object
|
|
245
|
+
* @param mode - Whether to use short aliases or long parameter names (default: `'short'`)
|
|
246
|
+
* @returns URL-encoded query string (without leading `?`)
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```ts
|
|
250
|
+
* // Using short aliases (default)
|
|
251
|
+
* buildQueryParams({ width: 800, height: 600, quality: 80 });
|
|
252
|
+
* // => 'w=800&h=600&q=80'
|
|
253
|
+
*
|
|
254
|
+
* // Using long parameter names
|
|
255
|
+
* buildQueryParams({ width: 800, format: 'webp' }, 'long');
|
|
256
|
+
* // => 'width=800&format=webp'
|
|
257
|
+
*
|
|
258
|
+
* // Nested options
|
|
259
|
+
* buildQueryParams({ border: { width: 2, color: '#ff0000' } });
|
|
260
|
+
* // => 'bo.w=2&bo.c=ff0000'
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
declare const buildQueryParams: (options: TransformationOptions, mode?: NameMode) => string;
|
|
264
|
+
/**
|
|
265
|
+
* Builds a complete transformation URL from a base URL and transformation options.
|
|
266
|
+
*
|
|
267
|
+
* Combines the configured base URL with query parameters generated from the
|
|
268
|
+
* transformation options. Handles the special `original` flag which returns
|
|
269
|
+
* the unmodified source image.
|
|
270
|
+
*
|
|
271
|
+
* @param config - Configuration containing the base URL for the image
|
|
272
|
+
* @param options - The transformation options to apply
|
|
273
|
+
* @param mode - Whether to use short aliases or long parameter names (default: `'short'`)
|
|
274
|
+
* @returns The complete URL string with query parameters
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```ts
|
|
278
|
+
* const config = { baseUrl: 'https://cdn.example.com/images/photo.jpg' };
|
|
279
|
+
*
|
|
280
|
+
* // Basic resize
|
|
281
|
+
* buildTransformationUrl(config, { width: 400, height: 300 });
|
|
282
|
+
* // => 'https://cdn.example.com/images/photo.jpg?w=400&h=300'
|
|
283
|
+
*
|
|
284
|
+
* // Multiple transformations
|
|
285
|
+
* buildTransformationUrl(config, {
|
|
286
|
+
* width: 800,
|
|
287
|
+
* format: 'webp',
|
|
288
|
+
* quality: 85,
|
|
289
|
+
* blur: 5
|
|
290
|
+
* });
|
|
291
|
+
* // => 'https://cdn.example.com/images/photo.jpg?w=800&f=webp&q=85&bl=5'
|
|
292
|
+
*
|
|
293
|
+
* // Request original image (no transformations)
|
|
294
|
+
* buildTransformationUrl(config, { original: true });
|
|
295
|
+
* // => 'https://cdn.example.com/images/photo.jpg?original'
|
|
296
|
+
*
|
|
297
|
+
* // Using long parameter names
|
|
298
|
+
* buildTransformationUrl(config, { width: 400 }, 'long');
|
|
299
|
+
* // => 'https://cdn.example.com/images/photo.jpg?width=400'
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
declare const buildTransformationUrl: (config: UrlBuilderConfig, options: TransformationOptions, mode?: NameMode) => string;
|
|
303
|
+
|
|
304
|
+
export { type BorderOptions, CROP_FOCUS, CROP_FOCUS_STRATEGY, CROP_OBJECT_TYPE, CROP_TYPE, 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 PixelFiddlerConfig, type Position, type ResizeMode, type ResizeOptions, type RotationAngle, type TextOptions, type TransformationOptions, type UrlBuilderConfig, type WatermarkOptions, buildQueryParams, buildTransformationUrl };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper type to extract values from const objects
|
|
3
|
+
*/
|
|
4
|
+
type ObjectValue<T> = T[keyof T];
|
|
5
|
+
/**
|
|
6
|
+
* Resize mode for image transformations
|
|
7
|
+
*/
|
|
8
|
+
type ResizeMode = 'FILL' | 'FIT' | 'PAD' | 'FORCE' | 'COVER';
|
|
9
|
+
/**
|
|
10
|
+
* Output image format - null or empty means AUTO
|
|
11
|
+
*/
|
|
12
|
+
type ImageFormat = 'jpeg' | 'png' | 'webp' | 'avif' | 'gif';
|
|
13
|
+
/**
|
|
14
|
+
* Position for text overlays and watermarks
|
|
15
|
+
*/
|
|
16
|
+
type Position = 'TOP_LEFT' | 'TOP_CENTER' | 'TOP_RIGHT' | 'CENTER_LEFT' | 'CENTER' | 'CENTER_RIGHT' | 'BOTTOM_LEFT' | 'BOTTOM_CENTER' | 'BOTTOM_RIGHT';
|
|
17
|
+
/**
|
|
18
|
+
* Crop focus type
|
|
19
|
+
*/
|
|
20
|
+
declare const CROP_FOCUS: {
|
|
21
|
+
readonly SPECIFIED: "SPECIFIED";
|
|
22
|
+
readonly AUTO: "AUTO";
|
|
23
|
+
};
|
|
24
|
+
type CropFocus = ObjectValue<typeof CROP_FOCUS>;
|
|
25
|
+
/**
|
|
26
|
+
* Crop focus strategy for AUTO focus
|
|
27
|
+
*/
|
|
28
|
+
declare const CROP_FOCUS_STRATEGY: {
|
|
29
|
+
readonly CENTER: "CENTER";
|
|
30
|
+
readonly SMART: "SMART";
|
|
31
|
+
readonly DETAIL: "DETAIL";
|
|
32
|
+
};
|
|
33
|
+
type CropFocusStrategy = ObjectValue<typeof CROP_FOCUS_STRATEGY>;
|
|
34
|
+
/**
|
|
35
|
+
* Crop type
|
|
36
|
+
*/
|
|
37
|
+
declare const CROP_TYPE: {
|
|
38
|
+
readonly SIMPLE: "SIMPLE";
|
|
39
|
+
readonly OBJECT: "OBJECT";
|
|
40
|
+
};
|
|
41
|
+
type CropType = ObjectValue<typeof CROP_TYPE>;
|
|
42
|
+
/**
|
|
43
|
+
* Object type for smart cropping
|
|
44
|
+
*/
|
|
45
|
+
declare const CROP_OBJECT_TYPE: {
|
|
46
|
+
readonly FACE: "FACE";
|
|
47
|
+
readonly UPPER_BODY: "UPPER_BODY";
|
|
48
|
+
};
|
|
49
|
+
type CropObjectType = ObjectValue<typeof CROP_OBJECT_TYPE>;
|
|
50
|
+
/**
|
|
51
|
+
* Rotation angle in degrees (clockwise)
|
|
52
|
+
*/
|
|
53
|
+
type RotationAngle = 0 | 90 | 180 | 270;
|
|
54
|
+
/**
|
|
55
|
+
* Device pixel ratio for high-density displays
|
|
56
|
+
*/
|
|
57
|
+
type DevicePixelRatio = 1 | 2 | 3 | 4;
|
|
58
|
+
/**
|
|
59
|
+
* Hex color string (with or without #)
|
|
60
|
+
*/
|
|
61
|
+
type HexColor = string;
|
|
62
|
+
/**
|
|
63
|
+
* Resize transformation options
|
|
64
|
+
*/
|
|
65
|
+
interface ResizeOptions {
|
|
66
|
+
/** Target width in pixels (1-4096) */
|
|
67
|
+
width?: number;
|
|
68
|
+
/** Target height in pixels (1-4096) */
|
|
69
|
+
height?: number;
|
|
70
|
+
/** Device pixel ratio for retina displays */
|
|
71
|
+
dpr?: DevicePixelRatio;
|
|
72
|
+
/** Resize strategy */
|
|
73
|
+
mode?: ResizeMode;
|
|
74
|
+
/** Background color for PAD mode (hex) */
|
|
75
|
+
background?: HexColor;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Format and quality options
|
|
79
|
+
*/
|
|
80
|
+
interface FormatOptions {
|
|
81
|
+
/** Output format */
|
|
82
|
+
format?: ImageFormat;
|
|
83
|
+
/** Quality level (1-100) */
|
|
84
|
+
quality?: number;
|
|
85
|
+
/** Strip metadata from output */
|
|
86
|
+
stripMetadata?: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Effect transformation options
|
|
90
|
+
*/
|
|
91
|
+
interface EffectOptions {
|
|
92
|
+
/** Gaussian blur intensity (0-100) */
|
|
93
|
+
blur?: number;
|
|
94
|
+
/** Brightness adjustment (0-100) */
|
|
95
|
+
brightness?: number;
|
|
96
|
+
/** Contrast adjustment (0-100) */
|
|
97
|
+
contrast?: number;
|
|
98
|
+
/** Convert to grayscale */
|
|
99
|
+
grayscale?: boolean;
|
|
100
|
+
/** Saturation adjustment (0-100) */
|
|
101
|
+
saturation?: number;
|
|
102
|
+
/** Apply sepia effect */
|
|
103
|
+
sepia?: boolean;
|
|
104
|
+
/** Sharpen intensity (0-100) */
|
|
105
|
+
sharpen?: number;
|
|
106
|
+
/** Add noise intensity (0-100) */
|
|
107
|
+
noise?: number;
|
|
108
|
+
/** Rotation angle */
|
|
109
|
+
rotate?: RotationAngle;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Border options
|
|
113
|
+
*/
|
|
114
|
+
interface BorderOptions {
|
|
115
|
+
/** Border width in pixels */
|
|
116
|
+
width?: number;
|
|
117
|
+
/** Border color (hex) */
|
|
118
|
+
color?: HexColor;
|
|
119
|
+
/** Corner radius in pixels (1-2000) */
|
|
120
|
+
radius?: number;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Crop focus position for SPECIFIED focus type
|
|
124
|
+
*/
|
|
125
|
+
interface CropFocusPosition {
|
|
126
|
+
x?: number;
|
|
127
|
+
y?: number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Crop focus options
|
|
131
|
+
*/
|
|
132
|
+
interface CropFocusOptions {
|
|
133
|
+
/** Focus type - SPECIFIED (manual position) or AUTO (strategy-based) */
|
|
134
|
+
type?: CropFocus;
|
|
135
|
+
/** Manual focus position when type is SPECIFIED */
|
|
136
|
+
position?: CropFocusPosition;
|
|
137
|
+
/** Auto focus strategy when type is AUTO */
|
|
138
|
+
strategy?: CropFocusStrategy;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Crop options
|
|
142
|
+
*/
|
|
143
|
+
interface CropOptions {
|
|
144
|
+
/** Crop type - SIMPLE or OBJECT */
|
|
145
|
+
type?: CropType;
|
|
146
|
+
/** Object type for OBJECT crop type */
|
|
147
|
+
objectType?: CropObjectType;
|
|
148
|
+
/** Focus options */
|
|
149
|
+
focus?: CropFocusOptions;
|
|
150
|
+
/** Crop width in pixels */
|
|
151
|
+
width?: number;
|
|
152
|
+
/** Crop height in pixels */
|
|
153
|
+
height?: number;
|
|
154
|
+
/** Apply crop after resize */
|
|
155
|
+
afterResize?: boolean;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Text overlay options
|
|
159
|
+
*/
|
|
160
|
+
interface TextOptions {
|
|
161
|
+
/** Text content to render */
|
|
162
|
+
text?: string;
|
|
163
|
+
/** Text color (hex) */
|
|
164
|
+
color?: HexColor;
|
|
165
|
+
/** Text opacity (0-100) */
|
|
166
|
+
opacity?: number;
|
|
167
|
+
/** Background color behind text (hex) */
|
|
168
|
+
background?: HexColor;
|
|
169
|
+
/** Background opacity (0-100) */
|
|
170
|
+
backgroundOpacity?: number;
|
|
171
|
+
/** Text size as percentage of image (1-100) */
|
|
172
|
+
size?: number;
|
|
173
|
+
/** Font family name */
|
|
174
|
+
font?: string;
|
|
175
|
+
/** Font size in pixels */
|
|
176
|
+
fontSize?: number;
|
|
177
|
+
/** Text position */
|
|
178
|
+
position?: Position;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Watermark options
|
|
182
|
+
*/
|
|
183
|
+
interface WatermarkOptions {
|
|
184
|
+
/** Watermark name (predefined in dashboard) */
|
|
185
|
+
name?: string;
|
|
186
|
+
/** Watermark opacity (0-100) */
|
|
187
|
+
opacity?: number;
|
|
188
|
+
/** Watermark size as percentage (1-100) */
|
|
189
|
+
size?: number;
|
|
190
|
+
/** Watermark width in pixels */
|
|
191
|
+
width?: number;
|
|
192
|
+
/** Watermark height in pixels */
|
|
193
|
+
height?: number;
|
|
194
|
+
/** Watermark position */
|
|
195
|
+
position?: Position;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* All transformation options combined
|
|
199
|
+
*/
|
|
200
|
+
interface TransformationOptions extends ResizeOptions, FormatOptions, EffectOptions {
|
|
201
|
+
/** Border options */
|
|
202
|
+
border?: BorderOptions;
|
|
203
|
+
/** Crop options */
|
|
204
|
+
crop?: CropOptions;
|
|
205
|
+
/** Text overlay options */
|
|
206
|
+
text?: TextOptions;
|
|
207
|
+
/** Watermark options */
|
|
208
|
+
watermark?: WatermarkOptions;
|
|
209
|
+
/** Use predefined transformation alias */
|
|
210
|
+
alias?: string;
|
|
211
|
+
/** Return original image without transformations */
|
|
212
|
+
original?: boolean;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Configuration for PixelFiddler client
|
|
216
|
+
*/
|
|
217
|
+
interface PixelFiddlerConfig {
|
|
218
|
+
/** Base URL for the CDN (defaults to https://cdn.pixel-fiddler.com) */
|
|
219
|
+
baseUrl?: string;
|
|
220
|
+
/** Secret key for URL signing */
|
|
221
|
+
signatureKey?: string;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* URL builder configuration (internal)
|
|
225
|
+
*/
|
|
226
|
+
interface UrlBuilderConfig extends PixelFiddlerConfig {
|
|
227
|
+
/** Resolved base URL */
|
|
228
|
+
baseUrl: string;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Determines which parameter names to use in the query string.
|
|
233
|
+
* - `'short'` - Use abbreviated aliases (e.g., `w`, `h`, `q`)
|
|
234
|
+
* - `'long'` - Use full parameter names (e.g., `width`, `height`, `quality`)
|
|
235
|
+
*/
|
|
236
|
+
type NameMode = 'short' | 'long';
|
|
237
|
+
/**
|
|
238
|
+
* Converts transformation options into a URL-encoded query string.
|
|
239
|
+
*
|
|
240
|
+
* Iterates through all defined parameters in the PARAMS config, extracts values
|
|
241
|
+
* from the options object using their paths, applies any normalization functions
|
|
242
|
+
* (e.g., stripping `#` from hex colors), and returns a URL-encoded query string.
|
|
243
|
+
*
|
|
244
|
+
* @param options - The transformation options object
|
|
245
|
+
* @param mode - Whether to use short aliases or long parameter names (default: `'short'`)
|
|
246
|
+
* @returns URL-encoded query string (without leading `?`)
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```ts
|
|
250
|
+
* // Using short aliases (default)
|
|
251
|
+
* buildQueryParams({ width: 800, height: 600, quality: 80 });
|
|
252
|
+
* // => 'w=800&h=600&q=80'
|
|
253
|
+
*
|
|
254
|
+
* // Using long parameter names
|
|
255
|
+
* buildQueryParams({ width: 800, format: 'webp' }, 'long');
|
|
256
|
+
* // => 'width=800&format=webp'
|
|
257
|
+
*
|
|
258
|
+
* // Nested options
|
|
259
|
+
* buildQueryParams({ border: { width: 2, color: '#ff0000' } });
|
|
260
|
+
* // => 'bo.w=2&bo.c=ff0000'
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
declare const buildQueryParams: (options: TransformationOptions, mode?: NameMode) => string;
|
|
264
|
+
/**
|
|
265
|
+
* Builds a complete transformation URL from a base URL and transformation options.
|
|
266
|
+
*
|
|
267
|
+
* Combines the configured base URL with query parameters generated from the
|
|
268
|
+
* transformation options. Handles the special `original` flag which returns
|
|
269
|
+
* the unmodified source image.
|
|
270
|
+
*
|
|
271
|
+
* @param config - Configuration containing the base URL for the image
|
|
272
|
+
* @param options - The transformation options to apply
|
|
273
|
+
* @param mode - Whether to use short aliases or long parameter names (default: `'short'`)
|
|
274
|
+
* @returns The complete URL string with query parameters
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```ts
|
|
278
|
+
* const config = { baseUrl: 'https://cdn.example.com/images/photo.jpg' };
|
|
279
|
+
*
|
|
280
|
+
* // Basic resize
|
|
281
|
+
* buildTransformationUrl(config, { width: 400, height: 300 });
|
|
282
|
+
* // => 'https://cdn.example.com/images/photo.jpg?w=400&h=300'
|
|
283
|
+
*
|
|
284
|
+
* // Multiple transformations
|
|
285
|
+
* buildTransformationUrl(config, {
|
|
286
|
+
* width: 800,
|
|
287
|
+
* format: 'webp',
|
|
288
|
+
* quality: 85,
|
|
289
|
+
* blur: 5
|
|
290
|
+
* });
|
|
291
|
+
* // => 'https://cdn.example.com/images/photo.jpg?w=800&f=webp&q=85&bl=5'
|
|
292
|
+
*
|
|
293
|
+
* // Request original image (no transformations)
|
|
294
|
+
* buildTransformationUrl(config, { original: true });
|
|
295
|
+
* // => 'https://cdn.example.com/images/photo.jpg?original'
|
|
296
|
+
*
|
|
297
|
+
* // Using long parameter names
|
|
298
|
+
* buildTransformationUrl(config, { width: 400 }, 'long');
|
|
299
|
+
* // => 'https://cdn.example.com/images/photo.jpg?width=400'
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
declare const buildTransformationUrl: (config: UrlBuilderConfig, options: TransformationOptions, mode?: NameMode) => string;
|
|
303
|
+
|
|
304
|
+
export { type BorderOptions, CROP_FOCUS, CROP_FOCUS_STRATEGY, CROP_OBJECT_TYPE, CROP_TYPE, 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 PixelFiddlerConfig, type Position, type ResizeMode, type ResizeOptions, type RotationAngle, type TextOptions, type TransformationOptions, type UrlBuilderConfig, type WatermarkOptions, buildQueryParams, buildTransformationUrl };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var CROP_FOCUS = {
|
|
3
|
+
SPECIFIED: "SPECIFIED",
|
|
4
|
+
AUTO: "AUTO"
|
|
5
|
+
};
|
|
6
|
+
var CROP_FOCUS_STRATEGY = {
|
|
7
|
+
CENTER: "CENTER",
|
|
8
|
+
SMART: "SMART",
|
|
9
|
+
DETAIL: "DETAIL"
|
|
10
|
+
};
|
|
11
|
+
var CROP_TYPE = {
|
|
12
|
+
SIMPLE: "SIMPLE",
|
|
13
|
+
OBJECT: "OBJECT"
|
|
14
|
+
};
|
|
15
|
+
var CROP_OBJECT_TYPE = {
|
|
16
|
+
FACE: "FACE",
|
|
17
|
+
UPPER_BODY: "UPPER_BODY"
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// src/utils.ts
|
|
21
|
+
var normalizeHex = (value) => value.replace("#", "");
|
|
22
|
+
var removeTrailingSlash = (baseUrl) => {
|
|
23
|
+
return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
24
|
+
};
|
|
25
|
+
var getNestedValue = (obj, path) => path.reduce(
|
|
26
|
+
(acc, key) => acc == null ? void 0 : acc[key],
|
|
27
|
+
obj
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// src/config.ts
|
|
31
|
+
var PARAMS = [
|
|
32
|
+
{ path: ["width"], long: "width", short: "w" },
|
|
33
|
+
{ path: ["height"], long: "height", short: "h" },
|
|
34
|
+
{ path: ["dpr"], long: "dpr", short: "dpr" },
|
|
35
|
+
{ path: ["mode"], long: "mode", short: "rm" },
|
|
36
|
+
{
|
|
37
|
+
path: ["background"],
|
|
38
|
+
long: "background",
|
|
39
|
+
short: "bg",
|
|
40
|
+
normalize: (v) => typeof v === "string" ? normalizeHex(v) : v
|
|
41
|
+
},
|
|
42
|
+
{ path: ["format"], long: "format", short: "f" },
|
|
43
|
+
{ path: ["quality"], long: "quality", short: "q" },
|
|
44
|
+
{ path: ["stripMetadata"], long: "stripMetadata", short: "st" },
|
|
45
|
+
{ path: ["blur"], long: "blur", short: "bl" },
|
|
46
|
+
{ path: ["brightness"], long: "brightness", short: "br" },
|
|
47
|
+
{ path: ["contrast"], long: "contrast", short: "ct" },
|
|
48
|
+
{ path: ["grayscale"], long: "grayscale", short: "gs" },
|
|
49
|
+
{ path: ["saturation"], long: "saturation", short: "sa" },
|
|
50
|
+
{ path: ["sepia"], long: "sepia", short: "sp" },
|
|
51
|
+
{ path: ["sharpen"], long: "sharpen", short: "sh" },
|
|
52
|
+
{ path: ["noise"], long: "noise", short: "ns" },
|
|
53
|
+
{ path: ["rotate"], long: "rotate", short: "rt" },
|
|
54
|
+
{ path: ["border", "width"], long: "border.width", short: "bo.w" },
|
|
55
|
+
{
|
|
56
|
+
path: ["border", "color"],
|
|
57
|
+
long: "border.color",
|
|
58
|
+
short: "bo.c",
|
|
59
|
+
normalize: (v) => typeof v === "string" ? normalizeHex(v) : v
|
|
60
|
+
},
|
|
61
|
+
{ path: ["border", "radius"], long: "border.radius", short: "bo.r" },
|
|
62
|
+
{ path: ["crop", "type"], long: "crop.type", short: "cr.t" },
|
|
63
|
+
{ path: ["crop", "objectType"], long: "crop.objectType", short: "cr.o" },
|
|
64
|
+
{ path: ["crop", "focus", "position", "x"], long: "crop.x", short: "cr.x" },
|
|
65
|
+
{ path: ["crop", "focus", "position", "y"], long: "crop.y", short: "cr.y" },
|
|
66
|
+
{ path: ["crop", "focus", "strategy"], long: "crop", short: "cr" },
|
|
67
|
+
{ path: ["crop", "width"], long: "crop.width", short: "cr.w" },
|
|
68
|
+
{ path: ["crop", "height"], long: "crop.height", short: "cr.h" },
|
|
69
|
+
{ path: ["crop", "afterResize"], long: "crop.afterResize", short: "cr.ar" },
|
|
70
|
+
{ path: ["text", "text"], long: "text", short: "tx" },
|
|
71
|
+
{
|
|
72
|
+
path: ["text", "color"],
|
|
73
|
+
long: "text.color",
|
|
74
|
+
short: "tx.c",
|
|
75
|
+
normalize: (v) => typeof v === "string" ? normalizeHex(v) : v
|
|
76
|
+
},
|
|
77
|
+
{ path: ["text", "opacity"], long: "text.opacity", short: "tx.o" },
|
|
78
|
+
{ path: ["text", "size"], long: "text.size", short: "tx.s" },
|
|
79
|
+
{ path: ["text", "font"], long: "text.font", short: "tx.f" },
|
|
80
|
+
{ path: ["text", "fontSize"], long: "text.fontSize", short: "tx.fs" },
|
|
81
|
+
{ path: ["text", "position"], long: "text.position", short: "tx.p" },
|
|
82
|
+
{
|
|
83
|
+
path: ["text", "background"],
|
|
84
|
+
long: "text.background",
|
|
85
|
+
short: "tx.bg",
|
|
86
|
+
normalize: (v) => typeof v === "string" ? normalizeHex(v) : v
|
|
87
|
+
},
|
|
88
|
+
{ path: ["text", "backgroundOpacity"], long: "text.backgroundOpacity", short: "tx.bg.o" },
|
|
89
|
+
{ path: ["watermark", "name"], long: "watermark", short: "wm" },
|
|
90
|
+
{ path: ["watermark", "opacity"], long: "watermark.opacity", short: "wm.o" },
|
|
91
|
+
{ path: ["watermark", "size"], long: "watermark.size", short: "wm.s" },
|
|
92
|
+
{ path: ["watermark", "width"], long: "watermark.width", short: "wm.w" },
|
|
93
|
+
{ path: ["watermark", "height"], long: "watermark.height", short: "wm.h" },
|
|
94
|
+
{ path: ["watermark", "position"], long: "watermark.position", short: "wm.p" }
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
// src/url-builder.ts
|
|
98
|
+
var buildQueryParams = (options, mode = "short") => {
|
|
99
|
+
const params = [];
|
|
100
|
+
for (const spec of PARAMS) {
|
|
101
|
+
const value = getNestedValue(options, spec.path);
|
|
102
|
+
if (value == null) continue;
|
|
103
|
+
const name = mode === "short" && spec.short ? spec.short : spec.long;
|
|
104
|
+
let normalized = value;
|
|
105
|
+
if ("normalize" in spec) {
|
|
106
|
+
normalized = spec.normalize(value);
|
|
107
|
+
}
|
|
108
|
+
params.push([name, String(normalized)]);
|
|
109
|
+
}
|
|
110
|
+
return new URLSearchParams(
|
|
111
|
+
params.map(([k, v]) => [k, v])
|
|
112
|
+
).toString();
|
|
113
|
+
};
|
|
114
|
+
var buildTransformationUrl = (config, options, mode = "short") => {
|
|
115
|
+
if (options.original) {
|
|
116
|
+
return `${config.baseUrl}?original`;
|
|
117
|
+
}
|
|
118
|
+
const query = buildQueryParams(options, mode);
|
|
119
|
+
const baseUrl = removeTrailingSlash(config.baseUrl);
|
|
120
|
+
return `${baseUrl}?${query}`;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export { CROP_FOCUS, CROP_FOCUS_STRATEGY, CROP_OBJECT_TYPE, CROP_TYPE, buildQueryParams, buildTransformationUrl };
|
|
124
|
+
//# sourceMappingURL=index.js.map
|
|
125
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/utils.ts","../src/config.ts","../src/url-builder.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\r\n * Helper type to extract values from const objects\r\n */\r\ntype ObjectValue<T> = T[keyof T];\r\n\r\n/**\r\n * Resize mode for image transformations\r\n */\r\nexport type ResizeMode = 'FILL' | 'FIT' | 'PAD' | 'FORCE' | 'COVER';\r\n\r\n/**\r\n * Output image format - null or empty means AUTO\r\n */\r\nexport type ImageFormat = 'jpeg' | 'png' | 'webp' | 'avif' | 'gif'\r\n\r\n/**\r\n * Position for text overlays and watermarks\r\n */\r\nexport type Position =\r\n | 'TOP_LEFT'\r\n | 'TOP_CENTER'\r\n | 'TOP_RIGHT'\r\n | 'CENTER_LEFT'\r\n | 'CENTER'\r\n | 'CENTER_RIGHT'\r\n | 'BOTTOM_LEFT'\r\n | 'BOTTOM_CENTER'\r\n | 'BOTTOM_RIGHT';\r\n\r\n/**\r\n * Crop focus type\r\n */\r\nexport const CROP_FOCUS = {\r\n SPECIFIED: 'SPECIFIED',\r\n AUTO: 'AUTO',\r\n} as const;\r\n\r\nexport type CropFocus = ObjectValue<typeof CROP_FOCUS>;\r\n\r\n/**\r\n * Crop focus strategy for AUTO focus\r\n */\r\nexport const CROP_FOCUS_STRATEGY = {\r\n CENTER: 'CENTER',\r\n SMART: 'SMART',\r\n DETAIL: 'DETAIL',\r\n} as const;\r\n\r\nexport type CropFocusStrategy = ObjectValue<typeof CROP_FOCUS_STRATEGY>;\r\n\r\n/**\r\n * Crop type\r\n */\r\nexport const CROP_TYPE = {\r\n SIMPLE: 'SIMPLE',\r\n OBJECT: 'OBJECT',\r\n} as const;\r\n\r\nexport type CropType = ObjectValue<typeof CROP_TYPE>;\r\n\r\n/**\r\n * Object type for smart cropping\r\n */\r\nexport const CROP_OBJECT_TYPE = {\r\n FACE: 'FACE',\r\n UPPER_BODY: 'UPPER_BODY',\r\n} as const;\r\n\r\nexport type CropObjectType = ObjectValue<typeof CROP_OBJECT_TYPE>;\r\n\r\n/**\r\n * Rotation angle in degrees (clockwise)\r\n */\r\nexport type RotationAngle = 0 | 90 | 180 | 270;\r\n\r\n/**\r\n * Device pixel ratio for high-density displays\r\n */\r\nexport type DevicePixelRatio = 1 | 2 | 3 | 4;\r\n\r\n/**\r\n * Hex color string (with or without #)\r\n */\r\nexport type HexColor = string;\r\n\r\n/**\r\n * Resize transformation options\r\n */\r\nexport interface ResizeOptions {\r\n /** Target width in pixels (1-4096) */\r\n width?: number;\r\n /** Target height in pixels (1-4096) */\r\n height?: number;\r\n /** Device pixel ratio for retina displays */\r\n dpr?: DevicePixelRatio;\r\n /** Resize strategy */\r\n mode?: ResizeMode;\r\n /** Background color for PAD mode (hex) */\r\n background?: HexColor;\r\n}\r\n\r\n/**\r\n * Format and quality options\r\n */\r\nexport interface FormatOptions {\r\n /** Output format */\r\n format?: ImageFormat;\r\n /** Quality level (1-100) */\r\n quality?: number;\r\n /** Strip metadata from output */\r\n stripMetadata?: boolean;\r\n}\r\n\r\n/**\r\n * Effect transformation options\r\n */\r\nexport interface EffectOptions {\r\n /** Gaussian blur intensity (0-100) */\r\n blur?: number;\r\n /** Brightness adjustment (0-100) */\r\n brightness?: number;\r\n /** Contrast adjustment (0-100) */\r\n contrast?: number;\r\n /** Convert to grayscale */\r\n grayscale?: boolean;\r\n /** Saturation adjustment (0-100) */\r\n saturation?: number;\r\n /** Apply sepia effect */\r\n sepia?: boolean;\r\n /** Sharpen intensity (0-100) */\r\n sharpen?: number;\r\n /** Add noise intensity (0-100) */\r\n noise?: number;\r\n /** Rotation angle */\r\n rotate?: RotationAngle;\r\n}\r\n\r\n/**\r\n * Border options\r\n */\r\nexport interface BorderOptions {\r\n /** Border width in pixels */\r\n width?: number;\r\n /** Border color (hex) */\r\n color?: HexColor;\r\n /** Corner radius in pixels (1-2000) */\r\n radius?: number;\r\n}\r\n\r\n/**\r\n * Crop focus position for SPECIFIED focus type\r\n */\r\nexport interface CropFocusPosition {\r\n x?: number;\r\n y?: number;\r\n}\r\n\r\n/**\r\n * Crop focus options\r\n */\r\nexport interface CropFocusOptions {\r\n /** Focus type - SPECIFIED (manual position) or AUTO (strategy-based) */\r\n type?: CropFocus;\r\n /** Manual focus position when type is SPECIFIED */\r\n position?: CropFocusPosition;\r\n /** Auto focus strategy when type is AUTO */\r\n strategy?: CropFocusStrategy;\r\n}\r\n\r\n/**\r\n * Crop options\r\n */\r\nexport interface CropOptions {\r\n /** Crop type - SIMPLE or OBJECT */\r\n type?: CropType;\r\n /** Object type for OBJECT crop type */\r\n objectType?: CropObjectType;\r\n /** Focus options */\r\n focus?: CropFocusOptions;\r\n /** Crop width in pixels */\r\n width?: number;\r\n /** Crop height in pixels */\r\n height?: number;\r\n /** Apply crop after resize */\r\n afterResize?: boolean;\r\n}\r\n\r\n/**\r\n * Text overlay options\r\n */\r\nexport interface TextOptions {\r\n /** Text content to render */\r\n text?: string;\r\n /** Text color (hex) */\r\n color?: HexColor;\r\n /** Text opacity (0-100) */\r\n opacity?: number;\r\n /** Background color behind text (hex) */\r\n background?: HexColor;\r\n /** Background opacity (0-100) */\r\n backgroundOpacity?: number;\r\n /** Text size as percentage of image (1-100) */\r\n size?: number;\r\n /** Font family name */\r\n font?: string;\r\n /** Font size in pixels */\r\n fontSize?: number;\r\n /** Text position */\r\n position?: Position;\r\n}\r\n\r\n/**\r\n * Watermark options\r\n */\r\nexport interface WatermarkOptions {\r\n /** Watermark name (predefined in dashboard) */\r\n name?: string;\r\n /** Watermark opacity (0-100) */\r\n opacity?: number;\r\n /** Watermark size as percentage (1-100) */\r\n size?: number;\r\n /** Watermark width in pixels */\r\n width?: number;\r\n /** Watermark height in pixels */\r\n height?: number;\r\n /** Watermark position */\r\n position?: Position;\r\n}\r\n\r\n/**\r\n * All transformation options combined\r\n */\r\nexport interface TransformationOptions\r\n extends ResizeOptions,\r\n FormatOptions,\r\n EffectOptions {\r\n /** Border options */\r\n border?: BorderOptions;\r\n /** Crop options */\r\n crop?: CropOptions;\r\n /** Text overlay options */\r\n text?: TextOptions;\r\n /** Watermark options */\r\n watermark?: WatermarkOptions;\r\n /** Use predefined transformation alias */\r\n alias?: string;\r\n /** Return original image without transformations */\r\n original?: boolean;\r\n}\r\n\r\n/**\r\n * Configuration for PixelFiddler client\r\n */\r\nexport interface PixelFiddlerConfig {\r\n /** Base URL for the CDN (defaults to https://cdn.pixel-fiddler.com) */\r\n baseUrl?: string;\r\n /** Secret key for URL signing */\r\n signatureKey?: string;\r\n}\r\n\r\n/**\r\n * URL builder configuration (internal)\r\n */\r\nexport interface UrlBuilderConfig extends PixelFiddlerConfig {\r\n /** Resolved base URL */\r\n baseUrl: string;\r\n}\r\n\r\n/**\r\n * Mapping of transformation options to query parameter names\r\n */\r\nexport interface QueryParameterMapping {\r\n /** Full parameter name */\r\n name: string;\r\n /** Short alias */\r\n alias?: string;\r\n}\r\n\r\n\r\n","import { Path, PathValue } from './util-types';\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","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\ntype 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 buildQueryParams = (\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://cdn.example.com/images/photo.jpg' };\r\n *\r\n * // Basic resize\r\n * buildTransformationUrl(config, { width: 400, height: 300 });\r\n * // => 'https://cdn.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://cdn.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://cdn.example.com/images/photo.jpg?original'\r\n *\r\n * // Using long parameter names\r\n * buildTransformationUrl(config, { width: 400 }, 'long');\r\n * // => 'https://cdn.example.com/images/photo.jpg?width=400'\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 if (options.original) {\r\n return `${config.baseUrl}?original`;\r\n }\r\n\r\n const query = buildQueryParams(options, mode);\r\n\r\n const baseUrl = removeTrailingSlash(config.baseUrl)\r\n return `${baseUrl}?${query}`;\r\n};\r\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pixelfiddler/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Core utilities for PixelFiddler image transformation SDK",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
26
|
+
"tsup": "^8.0.1",
|
|
27
|
+
"typescript": "^5.3.3",
|
|
28
|
+
"vitest": "^4.0.18"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"pixelfiddler",
|
|
32
|
+
"image",
|
|
33
|
+
"transformation",
|
|
34
|
+
"cdn",
|
|
35
|
+
"optimization",
|
|
36
|
+
"resize",
|
|
37
|
+
"crop",
|
|
38
|
+
"webp",
|
|
39
|
+
"avif"
|
|
40
|
+
],
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"author": "PixelFiddler",
|
|
43
|
+
"homepage": "https://pixel-fiddler.com",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/pixelfiddler/js-sdk/issues"
|
|
46
|
+
},
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/pixelfiddler/js-sdk.git",
|
|
50
|
+
"directory": "packages/core"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=18"
|
|
57
|
+
},
|
|
58
|
+
"sideEffects": false,
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsup",
|
|
61
|
+
"dev": "tsup --watch",
|
|
62
|
+
"test": "vitest run",
|
|
63
|
+
"test:watch": "vitest",
|
|
64
|
+
"test:coverage": "vitest run --coverage",
|
|
65
|
+
"typecheck": "tsc --noEmit",
|
|
66
|
+
"lint": "eslint src --ext .ts",
|
|
67
|
+
"clean": "rm -rf dist coverage"
|
|
68
|
+
}
|
|
69
|
+
}
|