@nonphoto/sanity-image 0.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Jonas Luebbers
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.
@@ -0,0 +1,527 @@
1
+ import * as valibot from 'valibot';
2
+ import { InferOutput } from 'valibot';
3
+
4
+ declare const transformationsSchema: Omit<valibot.ObjectSchema<{
5
+ readonly auto: valibot.LiteralSchema<"format", undefined>;
6
+ readonly background: valibot.StringSchema<undefined>;
7
+ readonly blur: valibot.NumberSchema<undefined>;
8
+ readonly crop: valibot.UnionSchema<[valibot.LiteralSchema<"top", undefined>, valibot.LiteralSchema<"bottom", undefined>, valibot.LiteralSchema<"left", undefined>, valibot.LiteralSchema<"right", undefined>, valibot.LiteralSchema<"center", undefined>, valibot.LiteralSchema<"focalpoint", undefined>, valibot.LiteralSchema<"entropy", undefined>], undefined>;
9
+ readonly download: valibot.UnionSchema<[valibot.StringSchema<undefined>, valibot.BooleanSchema<undefined>], undefined>;
10
+ readonly dpr: valibot.UnionSchema<[valibot.LiteralSchema<1, undefined>, valibot.LiteralSchema<2, undefined>, valibot.LiteralSchema<3, undefined>], undefined>;
11
+ readonly fit: valibot.UnionSchema<[valibot.LiteralSchema<"clip", undefined>, valibot.LiteralSchema<"crop", undefined>, valibot.LiteralSchema<"fill", undefined>, valibot.LiteralSchema<"fillmax", undefined>, valibot.LiteralSchema<"max", undefined>, valibot.LiteralSchema<"scale", undefined>, valibot.LiteralSchema<"min", undefined>], undefined>;
12
+ readonly flipHorizontal: valibot.BooleanSchema<undefined>;
13
+ readonly flipVertical: valibot.BooleanSchema<undefined>;
14
+ readonly focalPoint: valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>;
15
+ readonly format: valibot.UnionSchema<[valibot.LiteralSchema<"jpg", undefined>, valibot.LiteralSchema<"pjpg", undefined>, valibot.LiteralSchema<"png", undefined>, valibot.LiteralSchema<"webp", undefined>], undefined>;
16
+ readonly frame: valibot.NumberSchema<undefined>;
17
+ readonly height: valibot.NumberSchema<undefined>;
18
+ readonly invert: valibot.BooleanSchema<undefined>;
19
+ readonly maxHeight: valibot.NumberSchema<undefined>;
20
+ readonly maxWidth: valibot.NumberSchema<undefined>;
21
+ readonly minHeight: valibot.NumberSchema<undefined>;
22
+ readonly minWidth: valibot.NumberSchema<undefined>;
23
+ readonly orientation: valibot.UnionSchema<[valibot.LiteralSchema<0, undefined>, valibot.LiteralSchema<90, undefined>, valibot.LiteralSchema<180, undefined>, valibot.LiteralSchema<270, undefined>], undefined>;
24
+ readonly pad: valibot.NumberSchema<undefined>;
25
+ readonly quality: valibot.NumberSchema<undefined>;
26
+ readonly rect: valibot.ObjectSchema<{
27
+ readonly pos: valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>;
28
+ readonly size: valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>;
29
+ }, undefined>;
30
+ readonly saturation: valibot.NumberSchema<undefined>;
31
+ readonly sharpen: valibot.NumberSchema<undefined>;
32
+ readonly width: valibot.NumberSchema<undefined>;
33
+ }, undefined>, "~types" | "~run" | "~standard" | "entries"> & {
34
+ readonly entries: {
35
+ readonly auto: valibot.OptionalSchema<valibot.LiteralSchema<"format", undefined>, undefined>;
36
+ readonly background: valibot.OptionalSchema<valibot.StringSchema<undefined>, undefined>;
37
+ readonly blur: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
38
+ readonly crop: valibot.OptionalSchema<valibot.UnionSchema<[valibot.LiteralSchema<"top", undefined>, valibot.LiteralSchema<"bottom", undefined>, valibot.LiteralSchema<"left", undefined>, valibot.LiteralSchema<"right", undefined>, valibot.LiteralSchema<"center", undefined>, valibot.LiteralSchema<"focalpoint", undefined>, valibot.LiteralSchema<"entropy", undefined>], undefined>, undefined>;
39
+ readonly download: valibot.OptionalSchema<valibot.UnionSchema<[valibot.StringSchema<undefined>, valibot.BooleanSchema<undefined>], undefined>, undefined>;
40
+ readonly dpr: valibot.OptionalSchema<valibot.UnionSchema<[valibot.LiteralSchema<1, undefined>, valibot.LiteralSchema<2, undefined>, valibot.LiteralSchema<3, undefined>], undefined>, undefined>;
41
+ readonly fit: valibot.OptionalSchema<valibot.UnionSchema<[valibot.LiteralSchema<"clip", undefined>, valibot.LiteralSchema<"crop", undefined>, valibot.LiteralSchema<"fill", undefined>, valibot.LiteralSchema<"fillmax", undefined>, valibot.LiteralSchema<"max", undefined>, valibot.LiteralSchema<"scale", undefined>, valibot.LiteralSchema<"min", undefined>], undefined>, undefined>;
42
+ readonly flipHorizontal: valibot.OptionalSchema<valibot.BooleanSchema<undefined>, undefined>;
43
+ readonly flipVertical: valibot.OptionalSchema<valibot.BooleanSchema<undefined>, undefined>;
44
+ readonly focalPoint: valibot.OptionalSchema<valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>, undefined>;
45
+ readonly format: valibot.OptionalSchema<valibot.UnionSchema<[valibot.LiteralSchema<"jpg", undefined>, valibot.LiteralSchema<"pjpg", undefined>, valibot.LiteralSchema<"png", undefined>, valibot.LiteralSchema<"webp", undefined>], undefined>, undefined>;
46
+ readonly frame: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
47
+ readonly height: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
48
+ readonly invert: valibot.OptionalSchema<valibot.BooleanSchema<undefined>, undefined>;
49
+ readonly maxHeight: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
50
+ readonly maxWidth: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
51
+ readonly minHeight: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
52
+ readonly minWidth: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
53
+ readonly orientation: valibot.OptionalSchema<valibot.UnionSchema<[valibot.LiteralSchema<0, undefined>, valibot.LiteralSchema<90, undefined>, valibot.LiteralSchema<180, undefined>, valibot.LiteralSchema<270, undefined>], undefined>, undefined>;
54
+ readonly pad: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
55
+ readonly quality: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
56
+ readonly rect: valibot.OptionalSchema<valibot.ObjectSchema<{
57
+ readonly pos: valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>;
58
+ readonly size: valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>;
59
+ }, undefined>, undefined>;
60
+ readonly saturation: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
61
+ readonly sharpen: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
62
+ readonly width: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
63
+ };
64
+ readonly '~standard': valibot.StandardProps<{
65
+ auto?: "format" | undefined;
66
+ background?: string | undefined;
67
+ blur?: number | undefined;
68
+ crop?: "top" | "bottom" | "left" | "right" | "center" | "focalpoint" | "entropy" | undefined;
69
+ download?: string | boolean | undefined;
70
+ dpr?: 1 | 2 | 3 | undefined;
71
+ fit?: "fill" | "crop" | "clip" | "fillmax" | "max" | "scale" | "min" | undefined;
72
+ flipHorizontal?: boolean | undefined;
73
+ flipVertical?: boolean | undefined;
74
+ focalPoint?: [number, number] | undefined;
75
+ format?: "jpg" | "pjpg" | "png" | "webp" | undefined;
76
+ frame?: number | undefined;
77
+ height?: number | undefined;
78
+ invert?: boolean | undefined;
79
+ maxHeight?: number | undefined;
80
+ maxWidth?: number | undefined;
81
+ minHeight?: number | undefined;
82
+ minWidth?: number | undefined;
83
+ orientation?: 0 | 90 | 180 | 270 | undefined;
84
+ pad?: number | undefined;
85
+ quality?: number | undefined;
86
+ rect?: {
87
+ pos: [number, number];
88
+ size: [number, number];
89
+ } | undefined;
90
+ saturation?: number | undefined;
91
+ sharpen?: number | undefined;
92
+ width?: number | undefined;
93
+ }, {
94
+ auto?: "format" | undefined;
95
+ background?: string | undefined;
96
+ blur?: number | undefined;
97
+ crop?: "top" | "bottom" | "left" | "right" | "center" | "focalpoint" | "entropy" | undefined;
98
+ download?: string | boolean | undefined;
99
+ dpr?: 1 | 2 | 3 | undefined;
100
+ fit?: "fill" | "crop" | "clip" | "fillmax" | "max" | "scale" | "min" | undefined;
101
+ flipHorizontal?: boolean | undefined;
102
+ flipVertical?: boolean | undefined;
103
+ focalPoint?: [number, number] | undefined;
104
+ format?: "jpg" | "pjpg" | "png" | "webp" | undefined;
105
+ frame?: number | undefined;
106
+ height?: number | undefined;
107
+ invert?: boolean | undefined;
108
+ maxHeight?: number | undefined;
109
+ maxWidth?: number | undefined;
110
+ minHeight?: number | undefined;
111
+ minWidth?: number | undefined;
112
+ orientation?: 0 | 90 | 180 | 270 | undefined;
113
+ pad?: number | undefined;
114
+ quality?: number | undefined;
115
+ rect?: {
116
+ pos: [number, number];
117
+ size: [number, number];
118
+ } | undefined;
119
+ saturation?: number | undefined;
120
+ sharpen?: number | undefined;
121
+ width?: number | undefined;
122
+ }>;
123
+ readonly '~run': (dataset: valibot.UnknownDataset, config: valibot.Config<valibot.BaseIssue<unknown>>) => valibot.OutputDataset<{
124
+ auto?: "format" | undefined;
125
+ background?: string | undefined;
126
+ blur?: number | undefined;
127
+ crop?: "top" | "bottom" | "left" | "right" | "center" | "focalpoint" | "entropy" | undefined;
128
+ download?: string | boolean | undefined;
129
+ dpr?: 1 | 2 | 3 | undefined;
130
+ fit?: "fill" | "crop" | "clip" | "fillmax" | "max" | "scale" | "min" | undefined;
131
+ flipHorizontal?: boolean | undefined;
132
+ flipVertical?: boolean | undefined;
133
+ focalPoint?: [number, number] | undefined;
134
+ format?: "jpg" | "pjpg" | "png" | "webp" | undefined;
135
+ frame?: number | undefined;
136
+ height?: number | undefined;
137
+ invert?: boolean | undefined;
138
+ maxHeight?: number | undefined;
139
+ maxWidth?: number | undefined;
140
+ minHeight?: number | undefined;
141
+ minWidth?: number | undefined;
142
+ orientation?: 0 | 90 | 180 | 270 | undefined;
143
+ pad?: number | undefined;
144
+ quality?: number | undefined;
145
+ rect?: {
146
+ pos: [number, number];
147
+ size: [number, number];
148
+ } | undefined;
149
+ saturation?: number | undefined;
150
+ sharpen?: number | undefined;
151
+ width?: number | undefined;
152
+ }, valibot.NumberIssue | valibot.ObjectIssue | valibot.TupleIssue | valibot.StringIssue | valibot.LiteralIssue | valibot.UnionIssue<valibot.LiteralIssue> | valibot.BooleanIssue | valibot.UnionIssue<valibot.StringIssue | valibot.BooleanIssue>>;
153
+ readonly '~types'?: {
154
+ readonly input: {
155
+ auto?: "format" | undefined;
156
+ background?: string | undefined;
157
+ blur?: number | undefined;
158
+ crop?: "top" | "bottom" | "left" | "right" | "center" | "focalpoint" | "entropy" | undefined;
159
+ download?: string | boolean | undefined;
160
+ dpr?: 1 | 2 | 3 | undefined;
161
+ fit?: "fill" | "crop" | "clip" | "fillmax" | "max" | "scale" | "min" | undefined;
162
+ flipHorizontal?: boolean | undefined;
163
+ flipVertical?: boolean | undefined;
164
+ focalPoint?: [number, number] | undefined;
165
+ format?: "jpg" | "pjpg" | "png" | "webp" | undefined;
166
+ frame?: number | undefined;
167
+ height?: number | undefined;
168
+ invert?: boolean | undefined;
169
+ maxHeight?: number | undefined;
170
+ maxWidth?: number | undefined;
171
+ minHeight?: number | undefined;
172
+ minWidth?: number | undefined;
173
+ orientation?: 0 | 90 | 180 | 270 | undefined;
174
+ pad?: number | undefined;
175
+ quality?: number | undefined;
176
+ rect?: {
177
+ pos: [number, number];
178
+ size: [number, number];
179
+ } | undefined;
180
+ saturation?: number | undefined;
181
+ sharpen?: number | undefined;
182
+ width?: number | undefined;
183
+ };
184
+ readonly output: {
185
+ auto?: "format" | undefined;
186
+ background?: string | undefined;
187
+ blur?: number | undefined;
188
+ crop?: "top" | "bottom" | "left" | "right" | "center" | "focalpoint" | "entropy" | undefined;
189
+ download?: string | boolean | undefined;
190
+ dpr?: 1 | 2 | 3 | undefined;
191
+ fit?: "fill" | "crop" | "clip" | "fillmax" | "max" | "scale" | "min" | undefined;
192
+ flipHorizontal?: boolean | undefined;
193
+ flipVertical?: boolean | undefined;
194
+ focalPoint?: [number, number] | undefined;
195
+ format?: "jpg" | "pjpg" | "png" | "webp" | undefined;
196
+ frame?: number | undefined;
197
+ height?: number | undefined;
198
+ invert?: boolean | undefined;
199
+ maxHeight?: number | undefined;
200
+ maxWidth?: number | undefined;
201
+ minHeight?: number | undefined;
202
+ minWidth?: number | undefined;
203
+ orientation?: 0 | 90 | 180 | 270 | undefined;
204
+ pad?: number | undefined;
205
+ quality?: number | undefined;
206
+ rect?: {
207
+ pos: [number, number];
208
+ size: [number, number];
209
+ } | undefined;
210
+ saturation?: number | undefined;
211
+ sharpen?: number | undefined;
212
+ width?: number | undefined;
213
+ };
214
+ readonly issue: valibot.NumberIssue | valibot.ObjectIssue | valibot.TupleIssue | valibot.StringIssue | valibot.LiteralIssue | valibot.UnionIssue<valibot.LiteralIssue> | valibot.BooleanIssue | valibot.UnionIssue<valibot.StringIssue | valibot.BooleanIssue>;
215
+ } | undefined;
216
+ };
217
+ type Transformations = InferOutput<typeof transformationsSchema>;
218
+ declare function isTransformations(input: unknown): input is Transformations;
219
+ declare function transformationsToURLSearch({ auto, background, blur, crop, download, dpr, fit, flipHorizontal, flipVertical, focalPoint, format, frame, height, invert, maxHeight, maxWidth, minHeight, minWidth, orientation, pad, quality, rect, saturation, sharpen, width, }: Transformations): string;
220
+
221
+ type ImageSource = ImageObject | ImageAsset | AssetLike | ReferenceLike | string;
222
+ type AssetLike = InferOutput<typeof assetLikeSchema>;
223
+ type ReferenceLike = InferOutput<typeof referenceLikeSchema>;
224
+ type ImageObject = InferOutput<typeof imageObjectSchema>;
225
+ type ImageAsset = InferOutput<typeof imageAssetSchema>;
226
+ declare const assetLikeSchema: valibot.ObjectSchema<{
227
+ readonly _id: valibot.StringSchema<undefined>;
228
+ }, undefined>;
229
+ declare const referenceLikeSchema: valibot.ObjectSchema<{
230
+ readonly _ref: valibot.StringSchema<undefined>;
231
+ }, undefined>;
232
+ declare const imageObjectSchema: valibot.ObjectSchema<{
233
+ readonly asset: valibot.NullishSchema<valibot.UnionSchema<[valibot.ObjectSchema<{
234
+ readonly _id: valibot.StringSchema<undefined>;
235
+ }, undefined>, valibot.ObjectSchema<{
236
+ readonly _ref: valibot.StringSchema<undefined>;
237
+ }, undefined>, valibot.StringSchema<undefined>], undefined>, undefined>;
238
+ readonly crop: valibot.NullishSchema<valibot.ObjectSchema<{
239
+ readonly top: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
240
+ readonly bottom: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
241
+ readonly left: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
242
+ readonly right: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
243
+ }, undefined>, undefined>;
244
+ readonly hotspot: valibot.NullishSchema<valibot.ObjectSchema<{
245
+ readonly x: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
246
+ readonly y: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
247
+ readonly width: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
248
+ readonly height: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
249
+ }, undefined>, undefined>;
250
+ }, undefined>;
251
+ declare const imageAssetSchema: valibot.ObjectSchema<{
252
+ readonly _id: valibot.StringSchema<undefined>;
253
+ readonly assetId: valibot.StringSchema<undefined>;
254
+ readonly width: valibot.NumberSchema<undefined>;
255
+ readonly height: valibot.NumberSchema<undefined>;
256
+ readonly extension: valibot.StringSchema<undefined>;
257
+ readonly vanityName: valibot.NullishSchema<valibot.StringSchema<undefined>, undefined>;
258
+ readonly transformations: valibot.NullishSchema<Omit<valibot.ObjectSchema<{
259
+ readonly auto: valibot.LiteralSchema<"format", undefined>;
260
+ readonly background: valibot.StringSchema<undefined>;
261
+ readonly blur: valibot.NumberSchema<undefined>;
262
+ readonly crop: valibot.UnionSchema<[valibot.LiteralSchema<"top", undefined>, valibot.LiteralSchema<"bottom", undefined>, valibot.LiteralSchema<"left", undefined>, valibot.LiteralSchema<"right", undefined>, valibot.LiteralSchema<"center", undefined>, valibot.LiteralSchema<"focalpoint", undefined>, valibot.LiteralSchema<"entropy", undefined>], undefined>;
263
+ readonly download: valibot.UnionSchema<[valibot.StringSchema<undefined>, valibot.BooleanSchema<undefined>], undefined>;
264
+ readonly dpr: valibot.UnionSchema<[valibot.LiteralSchema<1, undefined>, valibot.LiteralSchema<2, undefined>, valibot.LiteralSchema<3, undefined>], undefined>;
265
+ readonly fit: valibot.UnionSchema<[valibot.LiteralSchema<"clip", undefined>, valibot.LiteralSchema<"crop", undefined>, valibot.LiteralSchema<"fill", undefined>, valibot.LiteralSchema<"fillmax", undefined>, valibot.LiteralSchema<"max", undefined>, valibot.LiteralSchema<"scale", undefined>, valibot.LiteralSchema<"min", undefined>], undefined>;
266
+ readonly flipHorizontal: valibot.BooleanSchema<undefined>;
267
+ readonly flipVertical: valibot.BooleanSchema<undefined>;
268
+ readonly focalPoint: valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>;
269
+ readonly format: valibot.UnionSchema<[valibot.LiteralSchema<"jpg", undefined>, valibot.LiteralSchema<"pjpg", undefined>, valibot.LiteralSchema<"png", undefined>, valibot.LiteralSchema<"webp", undefined>], undefined>;
270
+ readonly frame: valibot.NumberSchema<undefined>;
271
+ readonly height: valibot.NumberSchema<undefined>;
272
+ readonly invert: valibot.BooleanSchema<undefined>;
273
+ readonly maxHeight: valibot.NumberSchema<undefined>;
274
+ readonly maxWidth: valibot.NumberSchema<undefined>;
275
+ readonly minHeight: valibot.NumberSchema<undefined>;
276
+ readonly minWidth: valibot.NumberSchema<undefined>;
277
+ readonly orientation: valibot.UnionSchema<[valibot.LiteralSchema<0, undefined>, valibot.LiteralSchema<90, undefined>, valibot.LiteralSchema<180, undefined>, valibot.LiteralSchema<270, undefined>], undefined>;
278
+ readonly pad: valibot.NumberSchema<undefined>;
279
+ readonly quality: valibot.NumberSchema<undefined>;
280
+ readonly rect: valibot.ObjectSchema<{
281
+ readonly pos: valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>;
282
+ readonly size: valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>;
283
+ }, undefined>;
284
+ readonly saturation: valibot.NumberSchema<undefined>;
285
+ readonly sharpen: valibot.NumberSchema<undefined>;
286
+ readonly width: valibot.NumberSchema<undefined>;
287
+ }, undefined>, "~types" | "~run" | "~standard" | "entries"> & {
288
+ readonly entries: {
289
+ readonly auto: valibot.OptionalSchema<valibot.LiteralSchema<"format", undefined>, undefined>;
290
+ readonly background: valibot.OptionalSchema<valibot.StringSchema<undefined>, undefined>;
291
+ readonly blur: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
292
+ readonly crop: valibot.OptionalSchema<valibot.UnionSchema<[valibot.LiteralSchema<"top", undefined>, valibot.LiteralSchema<"bottom", undefined>, valibot.LiteralSchema<"left", undefined>, valibot.LiteralSchema<"right", undefined>, valibot.LiteralSchema<"center", undefined>, valibot.LiteralSchema<"focalpoint", undefined>, valibot.LiteralSchema<"entropy", undefined>], undefined>, undefined>;
293
+ readonly download: valibot.OptionalSchema<valibot.UnionSchema<[valibot.StringSchema<undefined>, valibot.BooleanSchema<undefined>], undefined>, undefined>;
294
+ readonly dpr: valibot.OptionalSchema<valibot.UnionSchema<[valibot.LiteralSchema<1, undefined>, valibot.LiteralSchema<2, undefined>, valibot.LiteralSchema<3, undefined>], undefined>, undefined>;
295
+ readonly fit: valibot.OptionalSchema<valibot.UnionSchema<[valibot.LiteralSchema<"clip", undefined>, valibot.LiteralSchema<"crop", undefined>, valibot.LiteralSchema<"fill", undefined>, valibot.LiteralSchema<"fillmax", undefined>, valibot.LiteralSchema<"max", undefined>, valibot.LiteralSchema<"scale", undefined>, valibot.LiteralSchema<"min", undefined>], undefined>, undefined>;
296
+ readonly flipHorizontal: valibot.OptionalSchema<valibot.BooleanSchema<undefined>, undefined>;
297
+ readonly flipVertical: valibot.OptionalSchema<valibot.BooleanSchema<undefined>, undefined>;
298
+ readonly focalPoint: valibot.OptionalSchema<valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>, undefined>;
299
+ readonly format: valibot.OptionalSchema<valibot.UnionSchema<[valibot.LiteralSchema<"jpg", undefined>, valibot.LiteralSchema<"pjpg", undefined>, valibot.LiteralSchema<"png", undefined>, valibot.LiteralSchema<"webp", undefined>], undefined>, undefined>;
300
+ readonly frame: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
301
+ readonly height: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
302
+ readonly invert: valibot.OptionalSchema<valibot.BooleanSchema<undefined>, undefined>;
303
+ readonly maxHeight: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
304
+ readonly maxWidth: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
305
+ readonly minHeight: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
306
+ readonly minWidth: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
307
+ readonly orientation: valibot.OptionalSchema<valibot.UnionSchema<[valibot.LiteralSchema<0, undefined>, valibot.LiteralSchema<90, undefined>, valibot.LiteralSchema<180, undefined>, valibot.LiteralSchema<270, undefined>], undefined>, undefined>;
308
+ readonly pad: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
309
+ readonly quality: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
310
+ readonly rect: valibot.OptionalSchema<valibot.ObjectSchema<{
311
+ readonly pos: valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>;
312
+ readonly size: valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>;
313
+ }, undefined>, undefined>;
314
+ readonly saturation: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
315
+ readonly sharpen: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
316
+ readonly width: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
317
+ };
318
+ readonly '~standard': valibot.StandardProps<{
319
+ auto?: "format" | undefined;
320
+ background?: string | undefined;
321
+ blur?: number | undefined;
322
+ crop?: "top" | "bottom" | "left" | "right" | "center" | "focalpoint" | "entropy" | undefined;
323
+ download?: string | boolean | undefined;
324
+ dpr?: 1 | 2 | 3 | undefined;
325
+ fit?: "fill" | "crop" | "clip" | "fillmax" | "max" | "scale" | "min" | undefined;
326
+ flipHorizontal?: boolean | undefined;
327
+ flipVertical?: boolean | undefined;
328
+ focalPoint?: [number, number] | undefined;
329
+ format?: "jpg" | "pjpg" | "png" | "webp" | undefined;
330
+ frame?: number | undefined;
331
+ height?: number | undefined;
332
+ invert?: boolean | undefined;
333
+ maxHeight?: number | undefined;
334
+ maxWidth?: number | undefined;
335
+ minHeight?: number | undefined;
336
+ minWidth?: number | undefined;
337
+ orientation?: 0 | 90 | 180 | 270 | undefined;
338
+ pad?: number | undefined;
339
+ quality?: number | undefined;
340
+ rect?: {
341
+ pos: [number, number];
342
+ size: [number, number];
343
+ } | undefined;
344
+ saturation?: number | undefined;
345
+ sharpen?: number | undefined;
346
+ width?: number | undefined;
347
+ }, {
348
+ auto?: "format" | undefined;
349
+ background?: string | undefined;
350
+ blur?: number | undefined;
351
+ crop?: "top" | "bottom" | "left" | "right" | "center" | "focalpoint" | "entropy" | undefined;
352
+ download?: string | boolean | undefined;
353
+ dpr?: 1 | 2 | 3 | undefined;
354
+ fit?: "fill" | "crop" | "clip" | "fillmax" | "max" | "scale" | "min" | undefined;
355
+ flipHorizontal?: boolean | undefined;
356
+ flipVertical?: boolean | undefined;
357
+ focalPoint?: [number, number] | undefined;
358
+ format?: "jpg" | "pjpg" | "png" | "webp" | undefined;
359
+ frame?: number | undefined;
360
+ height?: number | undefined;
361
+ invert?: boolean | undefined;
362
+ maxHeight?: number | undefined;
363
+ maxWidth?: number | undefined;
364
+ minHeight?: number | undefined;
365
+ minWidth?: number | undefined;
366
+ orientation?: 0 | 90 | 180 | 270 | undefined;
367
+ pad?: number | undefined;
368
+ quality?: number | undefined;
369
+ rect?: {
370
+ pos: [number, number];
371
+ size: [number, number];
372
+ } | undefined;
373
+ saturation?: number | undefined;
374
+ sharpen?: number | undefined;
375
+ width?: number | undefined;
376
+ }>;
377
+ readonly '~run': (dataset: valibot.UnknownDataset, config: valibot.Config<valibot.BaseIssue<unknown>>) => valibot.OutputDataset<{
378
+ auto?: "format" | undefined;
379
+ background?: string | undefined;
380
+ blur?: number | undefined;
381
+ crop?: "top" | "bottom" | "left" | "right" | "center" | "focalpoint" | "entropy" | undefined;
382
+ download?: string | boolean | undefined;
383
+ dpr?: 1 | 2 | 3 | undefined;
384
+ fit?: "fill" | "crop" | "clip" | "fillmax" | "max" | "scale" | "min" | undefined;
385
+ flipHorizontal?: boolean | undefined;
386
+ flipVertical?: boolean | undefined;
387
+ focalPoint?: [number, number] | undefined;
388
+ format?: "jpg" | "pjpg" | "png" | "webp" | undefined;
389
+ frame?: number | undefined;
390
+ height?: number | undefined;
391
+ invert?: boolean | undefined;
392
+ maxHeight?: number | undefined;
393
+ maxWidth?: number | undefined;
394
+ minHeight?: number | undefined;
395
+ minWidth?: number | undefined;
396
+ orientation?: 0 | 90 | 180 | 270 | undefined;
397
+ pad?: number | undefined;
398
+ quality?: number | undefined;
399
+ rect?: {
400
+ pos: [number, number];
401
+ size: [number, number];
402
+ } | undefined;
403
+ saturation?: number | undefined;
404
+ sharpen?: number | undefined;
405
+ width?: number | undefined;
406
+ }, valibot.NumberIssue | valibot.ObjectIssue | valibot.TupleIssue | valibot.StringIssue | valibot.LiteralIssue | valibot.UnionIssue<valibot.LiteralIssue> | valibot.BooleanIssue | valibot.UnionIssue<valibot.StringIssue | valibot.BooleanIssue>>;
407
+ readonly '~types'?: {
408
+ readonly input: {
409
+ auto?: "format" | undefined;
410
+ background?: string | undefined;
411
+ blur?: number | undefined;
412
+ crop?: "top" | "bottom" | "left" | "right" | "center" | "focalpoint" | "entropy" | undefined;
413
+ download?: string | boolean | undefined;
414
+ dpr?: 1 | 2 | 3 | undefined;
415
+ fit?: "fill" | "crop" | "clip" | "fillmax" | "max" | "scale" | "min" | undefined;
416
+ flipHorizontal?: boolean | undefined;
417
+ flipVertical?: boolean | undefined;
418
+ focalPoint?: [number, number] | undefined;
419
+ format?: "jpg" | "pjpg" | "png" | "webp" | undefined;
420
+ frame?: number | undefined;
421
+ height?: number | undefined;
422
+ invert?: boolean | undefined;
423
+ maxHeight?: number | undefined;
424
+ maxWidth?: number | undefined;
425
+ minHeight?: number | undefined;
426
+ minWidth?: number | undefined;
427
+ orientation?: 0 | 90 | 180 | 270 | undefined;
428
+ pad?: number | undefined;
429
+ quality?: number | undefined;
430
+ rect?: {
431
+ pos: [number, number];
432
+ size: [number, number];
433
+ } | undefined;
434
+ saturation?: number | undefined;
435
+ sharpen?: number | undefined;
436
+ width?: number | undefined;
437
+ };
438
+ readonly output: {
439
+ auto?: "format" | undefined;
440
+ background?: string | undefined;
441
+ blur?: number | undefined;
442
+ crop?: "top" | "bottom" | "left" | "right" | "center" | "focalpoint" | "entropy" | undefined;
443
+ download?: string | boolean | undefined;
444
+ dpr?: 1 | 2 | 3 | undefined;
445
+ fit?: "fill" | "crop" | "clip" | "fillmax" | "max" | "scale" | "min" | undefined;
446
+ flipHorizontal?: boolean | undefined;
447
+ flipVertical?: boolean | undefined;
448
+ focalPoint?: [number, number] | undefined;
449
+ format?: "jpg" | "pjpg" | "png" | "webp" | undefined;
450
+ frame?: number | undefined;
451
+ height?: number | undefined;
452
+ invert?: boolean | undefined;
453
+ maxHeight?: number | undefined;
454
+ maxWidth?: number | undefined;
455
+ minHeight?: number | undefined;
456
+ minWidth?: number | undefined;
457
+ orientation?: 0 | 90 | 180 | 270 | undefined;
458
+ pad?: number | undefined;
459
+ quality?: number | undefined;
460
+ rect?: {
461
+ pos: [number, number];
462
+ size: [number, number];
463
+ } | undefined;
464
+ saturation?: number | undefined;
465
+ sharpen?: number | undefined;
466
+ width?: number | undefined;
467
+ };
468
+ readonly issue: valibot.NumberIssue | valibot.ObjectIssue | valibot.TupleIssue | valibot.StringIssue | valibot.LiteralIssue | valibot.UnionIssue<valibot.LiteralIssue> | valibot.BooleanIssue | valibot.UnionIssue<valibot.StringIssue | valibot.BooleanIssue>;
469
+ } | undefined;
470
+ }, undefined>;
471
+ }, undefined>;
472
+ declare function isAssetLike(input: unknown): input is AssetLike;
473
+ declare function isReferenceLike(input: unknown): input is ReferenceLike;
474
+ declare function isImageObject(input: unknown): input is ImageObject;
475
+ declare function isImageAsset(input: unknown): input is ImageAsset;
476
+ declare function assetIdFromSource(source: ImageSource): string | undefined;
477
+ declare function parseAssetId(id: string): ImageAsset | undefined;
478
+ declare function imageAssetFromSource(source: ImageSource): ImageAsset | undefined;
479
+ declare function imageAssetWithTransformations(asset: ImageAsset, transformations: Transformations): ImageAsset;
480
+
481
+ declare const srcsetWidths: {
482
+ default: number[];
483
+ expanded: number[];
484
+ };
485
+
486
+ declare const cropSchema: valibot.ObjectSchema<{
487
+ readonly top: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
488
+ readonly bottom: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
489
+ readonly left: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
490
+ readonly right: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
491
+ }, undefined>;
492
+ type Crop = InferOutput<typeof cropSchema>;
493
+ declare function isCrop(input: unknown): input is Crop;
494
+
495
+ declare const hotspotSchema: valibot.ObjectSchema<{
496
+ readonly x: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
497
+ readonly y: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
498
+ readonly width: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
499
+ readonly height: valibot.OptionalSchema<valibot.NumberSchema<undefined>, undefined>;
500
+ }, undefined>;
501
+ type Hotspot = InferOutput<typeof hotspotSchema>;
502
+ declare function isHotspot(input: unknown): input is Hotspot;
503
+
504
+ interface SanityClientLike {
505
+ projectId: string;
506
+ dataset: string;
507
+ }
508
+ declare function imageUrl(client: SanityClientLike, asset: ImageAsset): string;
509
+ declare function imageSrcset(client: SanityClientLike, asset: ImageAsset, widths?: number[]): string | undefined;
510
+ /**
511
+ * Calculates the aspect ratio of an image, taking its transformations into account.
512
+ * @param asset - The asset to calculate the aspect ratio of
513
+ * @returns The aspect ratio of the image
514
+ * @todo This function currently ignores the `crop` mode settings including focal point
515
+ * and min/max height/width.
516
+ */
517
+ declare function imageAspectRatio(asset: ImageAsset): number;
518
+
519
+ declare const rectSchema: valibot.ObjectSchema<{
520
+ readonly pos: valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>;
521
+ readonly size: valibot.TupleSchema<[valibot.NumberSchema<undefined>, valibot.NumberSchema<undefined>], undefined>;
522
+ }, undefined>;
523
+ type Rect = InferOutput<typeof rectSchema>;
524
+ declare function isRect(input: unknown): input is Rect;
525
+ declare function rectFromCrop(asset: Pick<ImageAsset, "width" | "height">, crop: Crop): Rect;
526
+
527
+ export { type AssetLike, type Crop, type Hotspot, type ImageAsset, type ImageObject, type ImageSource, type Rect, type ReferenceLike, type SanityClientLike, type Transformations, assetIdFromSource, assetLikeSchema, cropSchema, hotspotSchema, imageAspectRatio, imageAssetFromSource, imageAssetSchema, imageAssetWithTransformations, imageObjectSchema, imageSrcset, imageUrl, isAssetLike, isCrop, isHotspot, isImageAsset, isImageObject, isRect, isReferenceLike, isTransformations, parseAssetId, rectFromCrop, rectSchema, referenceLikeSchema, srcsetWidths, transformationsSchema, transformationsToURLSearch };
package/dist/index.js ADDED
@@ -0,0 +1,333 @@
1
+ // src/asset.ts
2
+ import {
3
+ is as is5,
4
+ nullish,
5
+ number as number5,
6
+ object as object5,
7
+ string as string2,
8
+ union as union2
9
+ } from "valibot";
10
+
11
+ // src/crop.ts
12
+ import { is, number, object, optional } from "valibot";
13
+ var cropSchema = object({
14
+ top: optional(number()),
15
+ bottom: optional(number()),
16
+ left: optional(number()),
17
+ right: optional(number())
18
+ });
19
+ function isCrop(input) {
20
+ return is(cropSchema, input);
21
+ }
22
+
23
+ // src/hotspot.ts
24
+ import { is as is2, number as number2, object as object2, optional as optional2 } from "valibot";
25
+ var hotspotSchema = object2({
26
+ x: optional2(number2()),
27
+ y: optional2(number2()),
28
+ width: optional2(number2()),
29
+ height: optional2(number2())
30
+ });
31
+ function isHotspot(input) {
32
+ return is2(hotspotSchema, input);
33
+ }
34
+
35
+ // src/rect.ts
36
+ import { is as is3, number as number3, object as object3, tuple } from "valibot";
37
+ var rectSchema = object3({
38
+ pos: tuple([number3(), number3()]),
39
+ size: tuple([number3(), number3()])
40
+ });
41
+ function isRect(input) {
42
+ return is3(rectSchema, input);
43
+ }
44
+ function rectFromCrop(asset, crop) {
45
+ const left = Math.max(crop.left ?? 0, 0);
46
+ const right = Math.max(crop.right ?? 0, 0);
47
+ const top = Math.max(crop.top ?? 0, 0);
48
+ const bottom = Math.max(crop.bottom ?? 0, 0);
49
+ return {
50
+ pos: [left * asset.width, top * asset.height],
51
+ size: [(1 - left - right) * asset.width, (1 - top - bottom) * asset.height]
52
+ };
53
+ }
54
+
55
+ // src/transformations.ts
56
+ import {
57
+ boolean,
58
+ is as is4,
59
+ literal,
60
+ number as number4,
61
+ object as object4,
62
+ partial,
63
+ string,
64
+ tuple as tuple2,
65
+ union
66
+ } from "valibot";
67
+ var transformationsSchema = partial(
68
+ object4({
69
+ auto: literal("format"),
70
+ background: string(),
71
+ blur: number4(),
72
+ crop: union([
73
+ literal("top"),
74
+ literal("bottom"),
75
+ literal("left"),
76
+ literal("right"),
77
+ literal("center"),
78
+ literal("focalpoint"),
79
+ literal("entropy")
80
+ ]),
81
+ download: union([string(), boolean()]),
82
+ dpr: union([literal(1), literal(2), literal(3)]),
83
+ fit: union([
84
+ literal("clip"),
85
+ literal("crop"),
86
+ literal("fill"),
87
+ literal("fillmax"),
88
+ literal("max"),
89
+ literal("scale"),
90
+ literal("min")
91
+ ]),
92
+ flipHorizontal: boolean(),
93
+ flipVertical: boolean(),
94
+ focalPoint: tuple2([number4(), number4()]),
95
+ format: union([
96
+ literal("jpg"),
97
+ literal("pjpg"),
98
+ literal("png"),
99
+ literal("webp")
100
+ ]),
101
+ frame: number4(),
102
+ height: number4(),
103
+ invert: boolean(),
104
+ maxHeight: number4(),
105
+ maxWidth: number4(),
106
+ minHeight: number4(),
107
+ minWidth: number4(),
108
+ orientation: union([literal(0), literal(90), literal(180), literal(270)]),
109
+ pad: number4(),
110
+ quality: number4(),
111
+ rect: rectSchema,
112
+ saturation: number4(),
113
+ sharpen: number4(),
114
+ width: number4()
115
+ })
116
+ );
117
+ function isTransformations(input) {
118
+ return is4(transformationsSchema, input);
119
+ }
120
+ function entry(key, value) {
121
+ return value == null || value === false ? void 0 : [key, String(typeof value === "number" ? Math.round(value) : value)];
122
+ }
123
+ function transformationsToURLSearch({
124
+ auto,
125
+ background,
126
+ blur,
127
+ crop,
128
+ download,
129
+ dpr,
130
+ fit,
131
+ flipHorizontal,
132
+ flipVertical,
133
+ focalPoint,
134
+ format,
135
+ frame,
136
+ height,
137
+ invert,
138
+ maxHeight,
139
+ maxWidth,
140
+ minHeight,
141
+ minWidth,
142
+ orientation,
143
+ pad,
144
+ quality,
145
+ rect,
146
+ saturation,
147
+ sharpen,
148
+ width
149
+ }) {
150
+ return "?" + [
151
+ entry("auto", auto),
152
+ entry("bg", background),
153
+ entry("blur", blur),
154
+ entry("crop", crop),
155
+ entry("dl", download),
156
+ entry("dpr", dpr),
157
+ entry("fit", fit),
158
+ entry(
159
+ "flip",
160
+ flipHorizontal || flipVertical ? [flipHorizontal && "h", flipVertical && "v"].filter(Boolean).join("") : void 0
161
+ ),
162
+ entry("fm", format),
163
+ entry("fp-x", focalPoint?.[0]),
164
+ entry("fp-y", focalPoint?.[1]),
165
+ entry("frame", frame),
166
+ entry("h", height),
167
+ entry("invert", invert),
168
+ entry("max-h", maxHeight),
169
+ entry("max-w", maxWidth),
170
+ entry("min-h", minHeight),
171
+ entry("min-w", minWidth),
172
+ entry("or", orientation),
173
+ entry("pad", pad),
174
+ entry("q", quality),
175
+ entry(
176
+ "rect",
177
+ rect ? [rect.pos[0], rect.pos[1], rect.size[0], rect.size[1]].map(Math.round).join(",") : void 0
178
+ ),
179
+ entry("sat", saturation),
180
+ entry("sharp", sharpen),
181
+ entry("w", width)
182
+ ].filter((entry2) => entry2 != null).map((entry2) => entry2.join("=")).join("&");
183
+ }
184
+
185
+ // src/asset.ts
186
+ var assetLikeSchema = object5({
187
+ _id: string2()
188
+ });
189
+ var referenceLikeSchema = object5({
190
+ _ref: string2()
191
+ });
192
+ var imageObjectSchema = object5({
193
+ asset: nullish(union2([assetLikeSchema, referenceLikeSchema, string2()])),
194
+ crop: nullish(cropSchema),
195
+ hotspot: nullish(hotspotSchema)
196
+ });
197
+ var imageAssetSchema = object5({
198
+ _id: string2(),
199
+ assetId: string2(),
200
+ width: number5(),
201
+ height: number5(),
202
+ extension: string2(),
203
+ vanityName: nullish(string2()),
204
+ transformations: nullish(transformationsSchema)
205
+ });
206
+ function isAssetLike(input) {
207
+ return is5(assetLikeSchema, input);
208
+ }
209
+ function isReferenceLike(input) {
210
+ return is5(referenceLikeSchema, input);
211
+ }
212
+ function isImageObject(input) {
213
+ return is5(imageObjectSchema, input);
214
+ }
215
+ function isImageAsset(input) {
216
+ return is5(imageAssetSchema, input);
217
+ }
218
+ function assetIdFromSource(source) {
219
+ return typeof source === "string" ? source : isAssetLike(source) ? source._id : isReferenceLike(source) ? source._ref : isImageObject(source) && source.asset ? assetIdFromSource(source.asset) : void 0;
220
+ }
221
+ function parseAssetId(id) {
222
+ const matches = id.match(/^image-(\w+)-(\d+)x(\d+)-(\w+)$/);
223
+ if (matches) {
224
+ const [, assetId, width, height, extension] = matches;
225
+ return {
226
+ _id: id,
227
+ assetId,
228
+ width: Number(width),
229
+ height: Number(height),
230
+ extension
231
+ };
232
+ }
233
+ }
234
+ function imageAssetFromSource(source) {
235
+ if (isImageAsset(source)) {
236
+ return source;
237
+ } else {
238
+ const id = assetIdFromSource(source);
239
+ const asset = id ? parseAssetId(id) : void 0;
240
+ return asset ? imageAssetWithTransformations(asset, {
241
+ rect: typeof source === "object" && "crop" in source && source.crop ? rectFromCrop(asset, source.crop) : void 0
242
+ }) : void 0;
243
+ }
244
+ }
245
+ function imageAssetWithTransformations(asset, transformations) {
246
+ return {
247
+ ...asset,
248
+ transformations: { ...asset.transformations, ...transformations }
249
+ };
250
+ }
251
+
252
+ // src/constants.ts
253
+ var srcsetWidths = {
254
+ default: [2560, 1920, 1280, 960, 640, 480, 360, 240],
255
+ expanded: [
256
+ 3840,
257
+ 3200,
258
+ 2560,
259
+ 2048,
260
+ 1920,
261
+ 1668,
262
+ 1280,
263
+ 1080,
264
+ 960,
265
+ 720,
266
+ 640,
267
+ 480,
268
+ 360,
269
+ 240
270
+ ]
271
+ };
272
+
273
+ // src/image.ts
274
+ function imageUrl(client, asset) {
275
+ const url = new URL(
276
+ [
277
+ "https://cdn.sanity.io/images",
278
+ client.projectId,
279
+ client.dataset,
280
+ `${asset.assetId}-${asset.width}x${asset.height}.${asset.extension}`,
281
+ asset.vanityName
282
+ ].filter(Boolean).join("/")
283
+ );
284
+ if (asset.transformations) {
285
+ url.search = transformationsToURLSearch(asset.transformations);
286
+ }
287
+ return url.href;
288
+ }
289
+ function imageSrcset(client, asset, widths = srcsetWidths.default) {
290
+ return widths.sort((a, b) => a - b).filter((width) => width < asset.width).map(Math.round).map((width) => {
291
+ const url = imageUrl(
292
+ client,
293
+ imageAssetWithTransformations(asset, {
294
+ width
295
+ })
296
+ );
297
+ return `${url} ${width}w`;
298
+ }).join(",");
299
+ }
300
+ function imageAspectRatio(asset) {
301
+ const size = asset.transformations && ["crop", "fill", "fillmax", "scale", "min"].includes(
302
+ asset.transformations.fit ?? ""
303
+ ) && asset.transformations.width != null && asset.transformations.height != null ? [asset.transformations.width, asset.transformations.height] : asset.transformations?.rect ? asset.transformations.rect.size : [asset.width, asset.height];
304
+ return size[0] / size[1];
305
+ }
306
+ export {
307
+ assetIdFromSource,
308
+ assetLikeSchema,
309
+ cropSchema,
310
+ hotspotSchema,
311
+ imageAspectRatio,
312
+ imageAssetFromSource,
313
+ imageAssetSchema,
314
+ imageAssetWithTransformations,
315
+ imageObjectSchema,
316
+ imageSrcset,
317
+ imageUrl,
318
+ isAssetLike,
319
+ isCrop,
320
+ isHotspot,
321
+ isImageAsset,
322
+ isImageObject,
323
+ isRect,
324
+ isReferenceLike,
325
+ isTransformations,
326
+ parseAssetId,
327
+ rectFromCrop,
328
+ rectSchema,
329
+ referenceLikeSchema,
330
+ srcsetWidths,
331
+ transformationsSchema,
332
+ transformationsToURLSearch
333
+ };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@nonphoto/sanity-image",
3
+ "version": "0.0.0",
4
+ "author": "Jonas Luebbers <jonas@jonasluebbers.com> (https://www.jonasluebbers.com)",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "import": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "./src/*": "./src/*"
19
+ },
20
+ "files": [
21
+ "./dist/*",
22
+ "./src/*"
23
+ ],
24
+ "module": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "devDependencies": {
27
+ "semantic-release": "25.0.2",
28
+ "tsup": "8.5.0",
29
+ "typescript": "5.9.2"
30
+ },
31
+ "engines": {
32
+ "node": ">=20"
33
+ },
34
+ "dependencies": {
35
+ "valibot": "^1.1.0"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup ./src/index.ts --dts --format esm --clean",
39
+ "release": "semantic-release"
40
+ }
41
+ }
package/src/asset.ts ADDED
@@ -0,0 +1,123 @@
1
+ import {
2
+ InferOutput,
3
+ is,
4
+ nullish,
5
+ number,
6
+ object,
7
+ string,
8
+ union,
9
+ } from "valibot";
10
+ import { cropSchema } from "./crop";
11
+ import { hotspotSchema } from "./hotspot";
12
+ import { rectFromCrop } from "./rect";
13
+ import { Transformations, transformationsSchema } from "./transformations";
14
+
15
+ export type ImageSource =
16
+ | ImageObject
17
+ | ImageAsset
18
+ | AssetLike
19
+ | ReferenceLike
20
+ | string;
21
+
22
+ export type AssetLike = InferOutput<typeof assetLikeSchema>;
23
+
24
+ export type ReferenceLike = InferOutput<typeof referenceLikeSchema>;
25
+
26
+ export type ImageObject = InferOutput<typeof imageObjectSchema>;
27
+
28
+ export type ImageAsset = InferOutput<typeof imageAssetSchema>;
29
+
30
+ export const assetLikeSchema = object({
31
+ _id: string(),
32
+ });
33
+
34
+ export const referenceLikeSchema = object({
35
+ _ref: string(),
36
+ });
37
+
38
+ export const imageObjectSchema = object({
39
+ asset: nullish(union([assetLikeSchema, referenceLikeSchema, string()])),
40
+ crop: nullish(cropSchema),
41
+ hotspot: nullish(hotspotSchema),
42
+ });
43
+
44
+ export const imageAssetSchema = object({
45
+ _id: string(),
46
+ assetId: string(),
47
+ width: number(),
48
+ height: number(),
49
+ extension: string(),
50
+ vanityName: nullish(string()),
51
+ transformations: nullish(transformationsSchema),
52
+ });
53
+
54
+ export function isAssetLike(input: unknown): input is AssetLike {
55
+ return is(assetLikeSchema, input);
56
+ }
57
+
58
+ export function isReferenceLike(input: unknown): input is ReferenceLike {
59
+ return is(referenceLikeSchema, input);
60
+ }
61
+
62
+ export function isImageObject(input: unknown): input is ImageObject {
63
+ return is(imageObjectSchema, input);
64
+ }
65
+
66
+ export function isImageAsset(input: unknown): input is ImageAsset {
67
+ return is(imageAssetSchema, input);
68
+ }
69
+
70
+ export function assetIdFromSource(source: ImageSource): string | undefined {
71
+ return typeof source === "string"
72
+ ? source
73
+ : isAssetLike(source)
74
+ ? source._id
75
+ : isReferenceLike(source)
76
+ ? source._ref
77
+ : isImageObject(source) && source.asset
78
+ ? assetIdFromSource(source.asset)
79
+ : undefined;
80
+ }
81
+
82
+ export function parseAssetId(id: string): ImageAsset | undefined {
83
+ const matches = id.match(/^image-(\w+)-(\d+)x(\d+)-(\w+)$/);
84
+ if (matches) {
85
+ const [, assetId, width, height, extension] = matches;
86
+ return {
87
+ _id: id,
88
+ assetId,
89
+ width: Number(width),
90
+ height: Number(height),
91
+ extension,
92
+ };
93
+ }
94
+ }
95
+
96
+ export function imageAssetFromSource(
97
+ source: ImageSource,
98
+ ): ImageAsset | undefined {
99
+ if (isImageAsset(source)) {
100
+ return source;
101
+ } else {
102
+ const id = assetIdFromSource(source);
103
+ const asset = id ? parseAssetId(id) : undefined;
104
+ return asset
105
+ ? imageAssetWithTransformations(asset, {
106
+ rect:
107
+ typeof source === "object" && "crop" in source && source.crop
108
+ ? rectFromCrop(asset, source.crop)
109
+ : undefined,
110
+ })
111
+ : undefined;
112
+ }
113
+ }
114
+
115
+ export function imageAssetWithTransformations(
116
+ asset: ImageAsset,
117
+ transformations: Transformations,
118
+ ): ImageAsset {
119
+ return {
120
+ ...asset,
121
+ transformations: { ...asset.transformations, ...transformations },
122
+ };
123
+ }
@@ -0,0 +1,7 @@
1
+ export const srcsetWidths = {
2
+ default: [2560, 1920, 1280, 960, 640, 480, 360, 240],
3
+ expanded: [
4
+ 3840, 3200, 2560, 2048, 1920, 1668, 1280, 1080, 960, 720, 640, 480, 360,
5
+ 240,
6
+ ],
7
+ };
package/src/crop.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { InferOutput, is, number, object, optional } from "valibot";
2
+
3
+ export const cropSchema = object({
4
+ top: optional(number()),
5
+ bottom: optional(number()),
6
+ left: optional(number()),
7
+ right: optional(number()),
8
+ });
9
+
10
+ export type Crop = InferOutput<typeof cropSchema>;
11
+
12
+ export function isCrop(input: unknown): input is Crop {
13
+ return is(cropSchema, input);
14
+ }
package/src/hotspot.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { InferOutput, is, number, object, optional } from "valibot";
2
+
3
+ export const hotspotSchema = object({
4
+ x: optional(number()),
5
+ y: optional(number()),
6
+ width: optional(number()),
7
+ height: optional(number()),
8
+ });
9
+
10
+ export type Hotspot = InferOutput<typeof hotspotSchema>;
11
+
12
+ export function isHotspot(input: unknown): input is Hotspot {
13
+ return is(hotspotSchema, input);
14
+ }
package/src/image.ts ADDED
@@ -0,0 +1,69 @@
1
+ import { ImageAsset, imageAssetWithTransformations } from "./asset";
2
+ import { srcsetWidths } from "./constants";
3
+ import { transformationsToURLSearch } from "./transformations";
4
+
5
+ export interface SanityClientLike {
6
+ projectId: string;
7
+ dataset: string;
8
+ }
9
+
10
+ export function imageUrl(client: SanityClientLike, asset: ImageAsset): string {
11
+ const url = new URL(
12
+ [
13
+ "https://cdn.sanity.io/images",
14
+ client.projectId,
15
+ client.dataset,
16
+ `${asset.assetId}-${asset.width}x${asset.height}.${asset.extension}`,
17
+ asset.vanityName,
18
+ ]
19
+ .filter(Boolean)
20
+ .join("/"),
21
+ );
22
+ if (asset.transformations) {
23
+ url.search = transformationsToURLSearch(asset.transformations);
24
+ }
25
+ return url.href;
26
+ }
27
+
28
+ export function imageSrcset(
29
+ client: SanityClientLike,
30
+ asset: ImageAsset,
31
+ widths: number[] = srcsetWidths.default,
32
+ ): string | undefined {
33
+ return widths
34
+ .sort((a, b) => a - b)
35
+ .filter((width) => width < asset.width)
36
+ .map(Math.round)
37
+ .map((width) => {
38
+ const url = imageUrl(
39
+ client,
40
+ imageAssetWithTransformations(asset, {
41
+ width,
42
+ }),
43
+ );
44
+ return `${url} ${width}w`;
45
+ })
46
+ .join(",");
47
+ }
48
+
49
+ /**
50
+ * Calculates the aspect ratio of an image, taking its transformations into account.
51
+ * @param asset - The asset to calculate the aspect ratio of
52
+ * @returns The aspect ratio of the image
53
+ * @todo This function currently ignores the `crop` mode settings including focal point
54
+ * and min/max height/width.
55
+ */
56
+ export function imageAspectRatio(asset: ImageAsset): number {
57
+ const size: [number, number] =
58
+ asset.transformations &&
59
+ ["crop", "fill", "fillmax", "scale", "min"].includes(
60
+ asset.transformations.fit ?? "",
61
+ ) &&
62
+ asset.transformations.width != null &&
63
+ asset.transformations.height != null
64
+ ? [asset.transformations.width, asset.transformations.height]
65
+ : asset.transformations?.rect
66
+ ? asset.transformations.rect.size
67
+ : [asset.width, asset.height];
68
+ return size[0] / size[1];
69
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./asset";
2
+ export * from "./constants";
3
+ export * from "./crop";
4
+ export * from "./hotspot";
5
+ export * from "./image";
6
+ export * from "./rect";
7
+ export * from "./transformations";
package/src/rect.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { InferOutput, is, number, object, tuple } from "valibot";
2
+ import { ImageAsset } from "./asset";
3
+ import { Crop } from "./crop";
4
+
5
+ export const rectSchema = object({
6
+ pos: tuple([number(), number()]),
7
+ size: tuple([number(), number()]),
8
+ });
9
+
10
+ export type Rect = InferOutput<typeof rectSchema>;
11
+
12
+ export function isRect(input: unknown): input is Rect {
13
+ return is(rectSchema, input);
14
+ }
15
+
16
+ export function rectFromCrop(
17
+ asset: Pick<ImageAsset, "width" | "height">,
18
+ crop: Crop,
19
+ ): Rect {
20
+ const left = Math.max(crop.left ?? 0, 0);
21
+ const right = Math.max(crop.right ?? 0, 0);
22
+ const top = Math.max(crop.top ?? 0, 0);
23
+ const bottom = Math.max(crop.bottom ?? 0, 0);
24
+ return {
25
+ pos: [left * asset.width, top * asset.height],
26
+ size: [(1 - left - right) * asset.width, (1 - top - bottom) * asset.height],
27
+ };
28
+ }
@@ -0,0 +1,155 @@
1
+ import {
2
+ boolean,
3
+ InferOutput,
4
+ is,
5
+ literal,
6
+ number,
7
+ object,
8
+ partial,
9
+ string,
10
+ tuple,
11
+ union,
12
+ } from "valibot";
13
+ import { rectSchema } from "./rect";
14
+
15
+ export const transformationsSchema = partial(
16
+ object({
17
+ auto: literal("format"),
18
+ background: string(),
19
+ blur: number(),
20
+ crop: union([
21
+ literal("top"),
22
+ literal("bottom"),
23
+ literal("left"),
24
+ literal("right"),
25
+ literal("center"),
26
+ literal("focalpoint"),
27
+ literal("entropy"),
28
+ ]),
29
+ download: union([string(), boolean()]),
30
+ dpr: union([literal(1), literal(2), literal(3)]),
31
+ fit: union([
32
+ literal("clip"),
33
+ literal("crop"),
34
+ literal("fill"),
35
+ literal("fillmax"),
36
+ literal("max"),
37
+ literal("scale"),
38
+ literal("min"),
39
+ ]),
40
+ flipHorizontal: boolean(),
41
+ flipVertical: boolean(),
42
+ focalPoint: tuple([number(), number()]),
43
+ format: union([
44
+ literal("jpg"),
45
+ literal("pjpg"),
46
+ literal("png"),
47
+ literal("webp"),
48
+ ]),
49
+ frame: number(),
50
+ height: number(),
51
+ invert: boolean(),
52
+ maxHeight: number(),
53
+ maxWidth: number(),
54
+ minHeight: number(),
55
+ minWidth: number(),
56
+ orientation: union([literal(0), literal(90), literal(180), literal(270)]),
57
+ pad: number(),
58
+ quality: number(),
59
+ rect: rectSchema,
60
+ saturation: number(),
61
+ sharpen: number(),
62
+ width: number(),
63
+ }),
64
+ );
65
+
66
+ export type Transformations = InferOutput<typeof transformationsSchema>;
67
+
68
+ export function isTransformations(input: unknown): input is Transformations {
69
+ return is(transformationsSchema, input);
70
+ }
71
+
72
+ function entry(
73
+ key: string,
74
+ value: string | number | boolean | null | undefined,
75
+ ): [string, string] | undefined {
76
+ return value == null || value === false
77
+ ? undefined
78
+ : [key, String(typeof value === "number" ? Math.round(value) : value)];
79
+ }
80
+
81
+ export function transformationsToURLSearch({
82
+ auto,
83
+ background,
84
+ blur,
85
+ crop,
86
+ download,
87
+ dpr,
88
+ fit,
89
+ flipHorizontal,
90
+ flipVertical,
91
+ focalPoint,
92
+ format,
93
+ frame,
94
+ height,
95
+ invert,
96
+ maxHeight,
97
+ maxWidth,
98
+ minHeight,
99
+ minWidth,
100
+ orientation,
101
+ pad,
102
+ quality,
103
+ rect,
104
+ saturation,
105
+ sharpen,
106
+ width,
107
+ }: Transformations): string {
108
+ return (
109
+ "?" +
110
+ [
111
+ entry("auto", auto),
112
+ entry("bg", background),
113
+ entry("blur", blur),
114
+ entry("crop", crop),
115
+ entry("dl", download),
116
+ entry("dpr", dpr),
117
+ entry("fit", fit),
118
+ entry(
119
+ "flip",
120
+ flipHorizontal || flipVertical
121
+ ? [flipHorizontal && "h", flipVertical && "v"]
122
+ .filter(Boolean)
123
+ .join("")
124
+ : undefined,
125
+ ),
126
+ entry("fm", format),
127
+ entry("fp-x", focalPoint?.[0]),
128
+ entry("fp-y", focalPoint?.[1]),
129
+ entry("frame", frame),
130
+ entry("h", height),
131
+ entry("invert", invert),
132
+ entry("max-h", maxHeight),
133
+ entry("max-w", maxWidth),
134
+ entry("min-h", minHeight),
135
+ entry("min-w", minWidth),
136
+ entry("or", orientation),
137
+ entry("pad", pad),
138
+ entry("q", quality),
139
+ entry(
140
+ "rect",
141
+ rect
142
+ ? [rect.pos[0], rect.pos[1], rect.size[0], rect.size[1]]
143
+ .map(Math.round)
144
+ .join(",")
145
+ : undefined,
146
+ ),
147
+ entry("sat", saturation),
148
+ entry("sharp", sharpen),
149
+ entry("w", width),
150
+ ]
151
+ .filter((entry) => entry != null)
152
+ .map((entry) => entry.join("="))
153
+ .join("&")
154
+ );
155
+ }