@joycostudio/iris 0.0.0-stage → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.js ADDED
@@ -0,0 +1,328 @@
1
+ // packages/core/policy.ts
2
+ import { minimatch } from "minimatch";
3
+
4
+ // packages/core/placeholder.ts
5
+ function placeholderSettings(input) {
6
+ if (!input || input === "none") return null;
7
+ const options = input === "blur" ? {} : input;
8
+ return {
9
+ width: options.width ?? 10,
10
+ quality: options.quality ?? 40,
11
+ blur: options.blur ?? 12
12
+ };
13
+ }
14
+ function resolvePlaceholder({
15
+ asset,
16
+ placeholder
17
+ }) {
18
+ const settings = placeholderSettings(placeholder);
19
+ if (!settings) return null;
20
+ const url = asset.placeholders?.find(
21
+ (item) => item.width === settings.width && item.quality === settings.quality
22
+ )?.url ?? (settings.width === 10 && settings.quality === 40 ? asset.placeholder : void 0);
23
+ return url ? { url, blur: settings.blur } : null;
24
+ }
25
+
26
+ // packages/core/policy.ts
27
+ function irisFailure(code, message) {
28
+ return { ok: false, error: { code, message } };
29
+ }
30
+ function createIris(options) {
31
+ const recipes = { ...options.recipes };
32
+ const rules = [...options.rules ?? []];
33
+ const assets = [...options.assets ?? []];
34
+ for (const name of [options.fallback, ...rules.map((rule) => rule.recipe)]) {
35
+ if (!Object.hasOwn(recipes, name))
36
+ throw new Error(`Unknown Iris recipe: ${name}`);
37
+ }
38
+ for (const recipe of Object.values(recipes)) {
39
+ if (typeof recipe !== "function") validateRecipe(recipe);
40
+ }
41
+ for (const rule of rules) {
42
+ if (rule.when && typeof rule.when !== "function")
43
+ validateConditions(rule.when);
44
+ }
45
+ function selectRecipe(context, override) {
46
+ let ruleIndex = null;
47
+ let name = override ?? options.fallback;
48
+ try {
49
+ if (override === void 0) {
50
+ const index = rules.findIndex(
51
+ (rule) => (rule.include === void 0 || matches(rule.include, context)) && (rule.exclude === void 0 || !matches(rule.exclude, context)) && (rule.when === void 0 || (typeof rule.when === "function" ? predicate(rule.when(context)) : conditionsMatch(rule.when, context)))
52
+ );
53
+ if (index !== -1) {
54
+ ruleIndex = index;
55
+ name = rules[index].recipe;
56
+ }
57
+ }
58
+ if (!Object.hasOwn(recipes, name))
59
+ return irisFailure("unknown_recipe", `Unknown Iris recipe: ${name}`);
60
+ const registered = recipes[name];
61
+ const recipe = typeof registered === "function" ? registered(context) : registered;
62
+ validateRecipe(recipe);
63
+ return { ok: true, data: { recipe, name, ruleIndex } };
64
+ } catch (error) {
65
+ return irisFailure(
66
+ "invalid_policy",
67
+ error instanceof Error ? error.message : String(error)
68
+ );
69
+ }
70
+ }
71
+ return {
72
+ assets,
73
+ placeholderRequests: [
74
+ ...new Map(
75
+ [
76
+ placeholderSettings("blur"),
77
+ ...Object.values(recipes).flatMap((recipe) => {
78
+ const settings = typeof recipe === "function" ? null : placeholderSettings(recipe.placeholder);
79
+ return settings ? [settings] : [];
80
+ })
81
+ ].map(({ width, quality }) => [
82
+ `${width}:${quality}`,
83
+ { width, quality }
84
+ ])
85
+ ).values()
86
+ ],
87
+ select({ asset, usage = {}, recipe }) {
88
+ if (!validVariant({ ...asset, url: asset.src }))
89
+ return irisFailure("invalid_asset", "Invalid asset metadata");
90
+ if (usage.width !== void 0 && !positive(usage.width) || usage.dpr !== void 0 && !positive(usage.dpr))
91
+ return irisFailure(
92
+ "invalid_usage",
93
+ "Usage width and DPR must be positive finite numbers"
94
+ );
95
+ return selectRecipe(
96
+ {
97
+ src: asset.src,
98
+ asset,
99
+ variants: [...asset.variants ?? [], { ...asset, url: asset.src }],
100
+ usage
101
+ },
102
+ recipe
103
+ );
104
+ },
105
+ resolve({ asset: input, catalog, usage = {}, recipe: override }) {
106
+ const asset = typeof input === "string" ? [...catalog ?? [], ...assets].find(
107
+ (item) => typeof item !== "string" && (item.src === input || item.id === input) && "width" in item
108
+ ) : input;
109
+ if (!asset)
110
+ return irisFailure(
111
+ "asset_unprepared",
112
+ `Prepare or supply metadata for ${String(input)} before resolving`
113
+ );
114
+ if (!validVariant({ ...asset, url: asset.src }))
115
+ return irisFailure(
116
+ "invalid_asset",
117
+ "Asset metadata must contain a URL, positive dimensions and non-negative bytes"
118
+ );
119
+ if (usage.width !== void 0 && !positive(usage.width) || usage.dpr !== void 0 && !positive(usage.dpr)) {
120
+ return irisFailure(
121
+ "invalid_usage",
122
+ "Usage width and DPR must be positive finite numbers"
123
+ );
124
+ }
125
+ const original = {
126
+ url: asset.src,
127
+ width: asset.width,
128
+ height: asset.height,
129
+ bytes: asset.bytes,
130
+ format: asset.format,
131
+ progressive: asset.progressive,
132
+ alpha: asset.alpha,
133
+ animated: asset.animated
134
+ };
135
+ const variants = [...asset.variants ?? [], original];
136
+ if (!variants.every(validVariant))
137
+ return irisFailure(
138
+ "invalid_variant",
139
+ "Prepare variant metadata before resolving"
140
+ );
141
+ const unique = [
142
+ ...new Map(
143
+ variants.map((variant) => [variant.url, { ...variant }])
144
+ ).values()
145
+ ];
146
+ const context = {
147
+ src: asset.src,
148
+ asset,
149
+ variants: unique,
150
+ usage
151
+ };
152
+ try {
153
+ const selection = selectRecipe(context, override);
154
+ if (!selection.ok) return selection;
155
+ const { recipe, name, ruleIndex } = selection.data;
156
+ const target = usage.width === void 0 ? null : Math.ceil(usage.width * (usage.dpr ?? 1));
157
+ if (target !== null && !positive(target))
158
+ return irisFailure("invalid_usage", "Target width overflow");
159
+ const diagnostics = [];
160
+ const note = (code, message) => diagnostics.push({ code, message });
161
+ const width = target ?? asset.width;
162
+ if (target === null)
163
+ note(
164
+ "unknown_target",
165
+ "Layout width is unknown; selecting for original width"
166
+ );
167
+ const compatible = unique.filter(
168
+ (item) => (asset.alpha !== true || item.alpha === true) && (asset.animated !== true || item.animated === true)
169
+ );
170
+ const final = choose(compatible, width);
171
+ if (final.width < width)
172
+ note(
173
+ "undersized_final",
174
+ "The selected final is smaller than the requested resolution"
175
+ );
176
+ let preview = null;
177
+ if (recipe.preview && recipe.preview !== "none") {
178
+ const selection2 = recipe.preview;
179
+ const previewWidth = selection2.ratio === void 0 ? selection2.maxWidth ?? (selection2.url ? Infinity : 640) : Math.min(
180
+ Math.floor(width * selection2.ratio),
181
+ selection2.maxWidth ?? Infinity
182
+ );
183
+ const candidates = compatible.filter(
184
+ (item) => item.url !== final.url && item.width <= previewWidth && item.bytes < final.bytes
185
+ );
186
+ preview = recipe.preview.url ? candidates.find(
187
+ (item) => item.url === recipe.preview.url
188
+ ) ?? null : candidates.sort(
189
+ (a, b) => b.width - a.width || a.bytes - b.bytes || a.url.localeCompare(b.url)
190
+ )[0] ?? null;
191
+ if (!preview)
192
+ note(
193
+ "missing_preview",
194
+ "No distinct smaller preview exists; delivering final directly"
195
+ );
196
+ }
197
+ const placeholder = resolvePlaceholder({
198
+ asset,
199
+ placeholder: recipe.placeholder
200
+ });
201
+ if (recipe.placeholder && recipe.placeholder !== "none" && !placeholder)
202
+ note(
203
+ "missing_placeholder",
204
+ "No placeholder supplied; continuing without blur"
205
+ );
206
+ return {
207
+ ok: true,
208
+ data: {
209
+ src: asset.src,
210
+ recipe: name,
211
+ ruleIndex,
212
+ targetWidth: target,
213
+ placeholder: final.progressive ? null : placeholder,
214
+ preview: final.progressive ? null : preview,
215
+ final,
216
+ diagnostics
217
+ }
218
+ };
219
+ } catch (error) {
220
+ return irisFailure(
221
+ "invalid_policy",
222
+ error instanceof Error ? error.message : String(error)
223
+ );
224
+ }
225
+ }
226
+ };
227
+ }
228
+ function positive(value) {
229
+ return Number.isFinite(value) && value > 0;
230
+ }
231
+ function validVariant(value) {
232
+ return value && typeof value.url === "string" && !!value.url && positive(value.width) && positive(value.height) && Number.isFinite(value.bytes) && value.bytes >= 0;
233
+ }
234
+ function choose(variants, target) {
235
+ const adequate = variants.filter((item) => item.width >= target);
236
+ if (adequate.length)
237
+ return adequate.sort(
238
+ (a, b) => a.bytes - b.bytes || a.width - b.width || a.url.localeCompare(b.url)
239
+ )[0];
240
+ return [...variants].sort(
241
+ (a, b) => b.width - a.width || a.bytes - b.bytes || a.url.localeCompare(b.url)
242
+ )[0];
243
+ }
244
+ function predicate(value) {
245
+ if (typeof value !== "boolean")
246
+ throw new Error("Iris predicates must return a synchronous boolean");
247
+ return value;
248
+ }
249
+ function matches(input, context) {
250
+ const matchers = Array.isArray(input) ? input : [input];
251
+ return matchers.some((matcher) => {
252
+ if (typeof matcher === "function") return predicate(matcher(context));
253
+ if (matcher instanceof RegExp)
254
+ return new RegExp(matcher.source, matcher.flags).test(context.src);
255
+ if (context.src === matcher) return true;
256
+ return minimatch(context.src.split(/[?#]/, 1)[0], matcher, {
257
+ dot: true,
258
+ nonegate: true,
259
+ nocomment: true
260
+ });
261
+ });
262
+ }
263
+ function conditionsMatch(when, context) {
264
+ const { asset, usage } = context;
265
+ const values = {
266
+ sourceBytes: asset.bytes,
267
+ sourceWidth: asset.width,
268
+ renderedWidth: usage.width,
269
+ targetWidth: usage.width === void 0 ? void 0 : usage.width * (usage.dpr ?? 1)
270
+ };
271
+ for (const key of Object.keys(values)) {
272
+ const range = when[key];
273
+ const value = values[key];
274
+ if (range && (value === void 0 || range.min !== void 0 && value < range.min || range.max !== void 0 && value > range.max))
275
+ return false;
276
+ }
277
+ return (when.alpha === void 0 || when.alpha === asset.alpha) && (when.priority === void 0 || when.priority === (usage.priority ?? false)) && (when.format === void 0 || when.format === asset.format);
278
+ }
279
+ function validateConditions(when) {
280
+ const supported = [
281
+ "sourceBytes",
282
+ "sourceWidth",
283
+ "renderedWidth",
284
+ "targetWidth",
285
+ "alpha",
286
+ "priority",
287
+ "format"
288
+ ];
289
+ for (const [key, value] of Object.entries(when)) {
290
+ if (!supported.includes(key))
291
+ throw new Error(`Unknown Iris condition: ${key}`);
292
+ if (key === "alpha" || key === "priority") {
293
+ if (typeof value !== "boolean") throw new Error(`${key} must be boolean`);
294
+ continue;
295
+ }
296
+ if (key === "format") {
297
+ if (typeof value !== "string") throw new Error("format must be a string");
298
+ continue;
299
+ }
300
+ const range = value;
301
+ if (!range || typeof range !== "object" || Object.keys(range).some((key2) => !["min", "max"].includes(key2)) || Object.values(range).some(
302
+ (value2) => !Number.isFinite(value2) || value2 < 0
303
+ ) || range.min !== void 0 && range.max !== void 0 && range.min > range.max)
304
+ throw new Error(`Invalid Iris range: ${key}`);
305
+ }
306
+ }
307
+ function validateRecipe(recipe) {
308
+ if (!recipe || typeof recipe !== "object" || "then" in recipe)
309
+ throw new Error("Recipes must return synchronous objects");
310
+ if (Object.keys(recipe).some((key) => !["placeholder", "preview"].includes(key)))
311
+ throw new Error("Unknown recipe field");
312
+ const placeholder = recipe.placeholder;
313
+ if (placeholder !== void 0 && placeholder !== "none" && placeholder !== "blur") {
314
+ if (!placeholder || typeof placeholder !== "object" || Object.keys(placeholder).some(
315
+ (key) => !["blur", "width", "quality"].includes(key)
316
+ ) || placeholder.blur !== void 0 && (!Number.isFinite(placeholder.blur) || placeholder.blur < 0) || placeholder.width !== void 0 && (!Number.isSafeInteger(placeholder.width) || placeholder.width < 1) || placeholder.quality !== void 0 && (!Number.isInteger(placeholder.quality) || placeholder.quality < 1 || placeholder.quality > 100))
317
+ throw new Error("Invalid placeholder settings");
318
+ }
319
+ const preview = recipe.preview;
320
+ if (preview !== void 0 && preview !== "none" && (!preview || typeof preview !== "object" || Object.keys(preview).some(
321
+ (key) => !["url", "maxWidth", "ratio", "quality", "loadFirst", "minDisplayMs"].includes(key)
322
+ ) || preview.maxWidth !== void 0 && !positive(preview.maxWidth) || preview.quality !== void 0 && (!Number.isInteger(preview.quality) || preview.quality < 1 || preview.quality > 100) || preview.ratio !== void 0 && (!positive(preview.ratio) || preview.ratio >= 1) || preview.url !== void 0 && typeof preview.url !== "string" || preview.loadFirst !== void 0 && typeof preview.loadFirst !== "boolean" || preview.minDisplayMs !== void 0 && (!Number.isFinite(preview.minDisplayMs) || preview.minDisplayMs < 0)))
323
+ throw new Error("Invalid preview");
324
+ }
325
+ export {
326
+ createIris
327
+ };
328
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../packages/core/policy.ts","../packages/core/placeholder.ts"],"sourcesContent":["import { minimatch } from \"minimatch\"\nimport { placeholderSettings, resolvePlaceholder } from \"./placeholder\"\nimport type {\n IrisCatalogAsset,\n IrisConditions,\n IrisMatcher,\n IrisPolicy,\n IrisPolicyContext,\n IrisPolicyOptions,\n IrisPolicyDiagnostic,\n IrisRange,\n IrisRecipe,\n IrisRecipeSelection,\n IrisResult,\n IrisVariant,\n} from \"./policy-types\"\n\nexport function irisFailure(code: string, message: string): IrisResult<never> {\n return { ok: false, error: { code, message } }\n}\n\n/** First matching rule wins. Construction is synchronous and performs no I/O. */\nexport function createIris<Custom = Record<string, unknown>>(\n options: IrisPolicyOptions<Custom>\n): IrisPolicy<Custom> {\n const recipes = { ...options.recipes }\n const rules = [...(options.rules ?? [])]\n const assets = [...(options.assets ?? [])]\n for (const name of [options.fallback, ...rules.map((rule) => rule.recipe)]) {\n if (!Object.hasOwn(recipes, name))\n throw new Error(`Unknown Iris recipe: ${name}`)\n }\n for (const recipe of Object.values(recipes)) {\n if (typeof recipe !== \"function\") validateRecipe(recipe)\n }\n for (const rule of rules) {\n if (rule.when && typeof rule.when !== \"function\")\n validateConditions(rule.when)\n }\n function selectRecipe(\n context: IrisPolicyContext<Custom>,\n override?: string\n ): IrisResult<IrisRecipeSelection> {\n let ruleIndex: number | null = null\n let name = override ?? options.fallback\n try {\n if (override === undefined) {\n const index = rules.findIndex(\n (rule) =>\n (rule.include === undefined || matches(rule.include, context)) &&\n (rule.exclude === undefined || !matches(rule.exclude, context)) &&\n (rule.when === undefined ||\n (typeof rule.when === \"function\"\n ? predicate(rule.when(context))\n : conditionsMatch(rule.when, context)))\n )\n if (index !== -1) {\n ruleIndex = index\n name = rules[index].recipe\n }\n }\n if (!Object.hasOwn(recipes, name))\n return irisFailure(\"unknown_recipe\", `Unknown Iris recipe: ${name}`)\n const registered = recipes[name]\n const recipe =\n typeof registered === \"function\" ? registered(context) : registered\n validateRecipe(recipe)\n return { ok: true, data: { recipe, name, ruleIndex } }\n } catch (error) {\n return irisFailure(\n \"invalid_policy\",\n error instanceof Error ? error.message : String(error)\n )\n }\n }\n return {\n assets,\n placeholderRequests: [\n ...new Map(\n [\n placeholderSettings(\"blur\")!,\n ...Object.values(recipes).flatMap((recipe) => {\n const settings =\n typeof recipe === \"function\"\n ? null\n : placeholderSettings(recipe.placeholder)\n return settings ? [settings] : []\n }),\n ].map(({ width, quality }) => [\n `${width}:${quality}`,\n { width, quality },\n ])\n ).values(),\n ],\n select({ asset, usage = {}, recipe }) {\n if (!validVariant({ ...asset, url: asset.src }))\n return irisFailure(\"invalid_asset\", \"Invalid asset metadata\")\n if (\n (usage.width !== undefined && !positive(usage.width)) ||\n (usage.dpr !== undefined && !positive(usage.dpr))\n )\n return irisFailure(\n \"invalid_usage\",\n \"Usage width and DPR must be positive finite numbers\"\n )\n return selectRecipe(\n {\n src: asset.src,\n asset,\n variants: [...(asset.variants ?? []), { ...asset, url: asset.src }],\n usage,\n },\n recipe\n )\n },\n resolve({ asset: input, catalog, usage = {}, recipe: override }) {\n const asset =\n typeof input === \"string\"\n ? ([...(catalog ?? []), ...assets].find(\n (item) =>\n typeof item !== \"string\" &&\n (item.src === input || item.id === input) &&\n \"width\" in item\n ) as IrisCatalogAsset | undefined)\n : input\n if (!asset)\n return irisFailure(\n \"asset_unprepared\",\n `Prepare or supply metadata for ${String(input)} before resolving`\n )\n if (!validVariant({ ...asset, url: asset.src }))\n return irisFailure(\n \"invalid_asset\",\n \"Asset metadata must contain a URL, positive dimensions and non-negative bytes\"\n )\n if (\n (usage.width !== undefined && !positive(usage.width)) ||\n (usage.dpr !== undefined && !positive(usage.dpr))\n ) {\n return irisFailure(\n \"invalid_usage\",\n \"Usage width and DPR must be positive finite numbers\"\n )\n }\n const original: IrisVariant = {\n url: asset.src,\n width: asset.width,\n height: asset.height,\n bytes: asset.bytes,\n format: asset.format,\n progressive: asset.progressive,\n alpha: asset.alpha,\n animated: asset.animated,\n }\n const variants = [...(asset.variants ?? []), original]\n if (!variants.every(validVariant))\n return irisFailure(\n \"invalid_variant\",\n \"Prepare variant metadata before resolving\"\n )\n // Preserve original metadata for its URL and avoid duplicate downloads.\n const unique = [\n ...new Map(\n variants.map((variant) => [variant.url, { ...variant }])\n ).values(),\n ]\n const context: IrisPolicyContext<Custom> = {\n src: asset.src,\n asset,\n variants: unique,\n usage,\n }\n try {\n const selection = selectRecipe(context, override)\n if (!selection.ok) return selection\n const { recipe, name, ruleIndex } = selection.data\n const target =\n usage.width === undefined\n ? null\n : Math.ceil(usage.width * (usage.dpr ?? 1))\n if (target !== null && !positive(target))\n return irisFailure(\"invalid_usage\", \"Target width overflow\")\n const diagnostics: IrisPolicyDiagnostic[] = []\n const note = (code: IrisPolicyDiagnostic[\"code\"], message: string) =>\n diagnostics.push({ code, message })\n // Unknown layout preserves source resolution rather than guessing a thumbnail.\n const width = target ?? asset.width\n if (target === null)\n note(\n \"unknown_target\",\n \"Layout width is unknown; selecting for original width\"\n )\n const compatible = unique.filter(\n (item) =>\n (asset.alpha !== true || item.alpha === true) &&\n (asset.animated !== true || item.animated === true)\n )\n const final = choose(compatible, width)\n if (final.width < width)\n note(\n \"undersized_final\",\n \"The selected final is smaller than the requested resolution\"\n )\n let preview: IrisVariant | null = null\n if (recipe.preview && recipe.preview !== \"none\") {\n const selection = recipe.preview\n const previewWidth =\n selection.ratio === undefined\n ? (selection.maxWidth ?? (selection.url ? Infinity : 640))\n : Math.min(\n Math.floor(width * selection.ratio),\n selection.maxWidth ?? Infinity\n )\n const candidates = compatible.filter(\n (item) =>\n item.url !== final.url &&\n item.width <= previewWidth &&\n item.bytes < final.bytes\n )\n preview = recipe.preview.url\n ? (candidates.find(\n (item) => item.url === (recipe.preview as { url?: string }).url\n ) ?? null)\n : (candidates.sort(\n (a, b) =>\n b.width - a.width ||\n a.bytes - b.bytes ||\n a.url.localeCompare(b.url)\n )[0] ?? null)\n if (!preview)\n note(\n \"missing_preview\",\n \"No distinct smaller preview exists; delivering final directly\"\n )\n }\n const placeholder = resolvePlaceholder({\n asset,\n placeholder: recipe.placeholder,\n })\n if (recipe.placeholder && recipe.placeholder !== \"none\" && !placeholder)\n note(\n \"missing_placeholder\",\n \"No placeholder supplied; continuing without blur\"\n )\n return {\n ok: true,\n data: {\n src: asset.src,\n recipe: name,\n ruleIndex,\n targetWidth: target,\n placeholder: final.progressive ? null : placeholder,\n preview: final.progressive ? null : preview,\n final,\n diagnostics,\n },\n }\n } catch (error) {\n return irisFailure(\n \"invalid_policy\",\n error instanceof Error ? error.message : String(error)\n )\n }\n },\n }\n}\n\nfunction positive(value: number) {\n return Number.isFinite(value) && value > 0\n}\nexport function validVariant(value: IrisVariant) {\n return (\n value &&\n typeof value.url === \"string\" &&\n !!value.url &&\n positive(value.width) &&\n positive(value.height) &&\n Number.isFinite(value.bytes) &&\n value.bytes >= 0\n )\n}\nfunction choose(variants: IrisVariant[], target: number) {\n const adequate = variants.filter((item) => item.width >= target)\n if (adequate.length)\n return adequate.sort(\n (a, b) =>\n a.bytes - b.bytes || a.width - b.width || a.url.localeCompare(b.url)\n )[0]\n return [...variants].sort(\n (a, b) =>\n b.width - a.width || a.bytes - b.bytes || a.url.localeCompare(b.url)\n )[0]\n}\nfunction predicate(value: unknown): boolean {\n if (typeof value !== \"boolean\")\n throw new Error(\"Iris predicates must return a synchronous boolean\")\n return value\n}\nfunction matches<T>(\n input: IrisMatcher<T> | readonly IrisMatcher<T>[],\n context: IrisPolicyContext<T>\n): boolean {\n const matchers = Array.isArray(input) ? input : [input as IrisMatcher<T>]\n return matchers.some((matcher) => {\n if (typeof matcher === \"function\") return predicate(matcher(context))\n if (matcher instanceof RegExp)\n return new RegExp(matcher.source, matcher.flags).test(context.src)\n if (context.src === matcher) return true\n // Globs ignore query/hash; regexes and callbacks receive the original URL.\n return minimatch(context.src.split(/[?#]/, 1)[0], matcher, {\n dot: true,\n nonegate: true,\n nocomment: true,\n })\n })\n}\nfunction conditionsMatch<T>(\n when: IrisConditions,\n context: IrisPolicyContext<T>\n) {\n const { asset, usage } = context\n const values = {\n sourceBytes: asset.bytes,\n sourceWidth: asset.width,\n renderedWidth: usage.width,\n targetWidth:\n usage.width === undefined ? undefined : usage.width * (usage.dpr ?? 1),\n }\n for (const key of Object.keys(values) as (keyof typeof values)[]) {\n const range = when[key]\n const value = values[key]\n if (\n range &&\n (value === undefined ||\n (range.min !== undefined && value < range.min) ||\n (range.max !== undefined && value > range.max))\n )\n return false\n }\n return (\n (when.alpha === undefined || when.alpha === asset.alpha) &&\n (when.priority === undefined ||\n when.priority === (usage.priority ?? false)) &&\n (when.format === undefined || when.format === asset.format)\n )\n}\nfunction validateConditions(when: IrisConditions) {\n const supported = [\n \"sourceBytes\",\n \"sourceWidth\",\n \"renderedWidth\",\n \"targetWidth\",\n \"alpha\",\n \"priority\",\n \"format\",\n ]\n for (const [key, value] of Object.entries(when)) {\n if (!supported.includes(key))\n throw new Error(`Unknown Iris condition: ${key}`)\n if (key === \"alpha\" || key === \"priority\") {\n if (typeof value !== \"boolean\") throw new Error(`${key} must be boolean`)\n continue\n }\n if (key === \"format\") {\n if (typeof value !== \"string\") throw new Error(\"format must be a string\")\n continue\n }\n const range = value as IrisRange\n if (\n !range ||\n typeof range !== \"object\" ||\n Object.keys(range).some((key) => ![\"min\", \"max\"].includes(key)) ||\n Object.values(range).some(\n (value) => !Number.isFinite(value) || value < 0\n ) ||\n (range.min !== undefined &&\n range.max !== undefined &&\n range.min > range.max)\n )\n throw new Error(`Invalid Iris range: ${key}`)\n }\n}\nfunction validateRecipe(recipe: IrisRecipe) {\n if (!recipe || typeof recipe !== \"object\" || \"then\" in recipe)\n throw new Error(\"Recipes must return synchronous objects\")\n if (\n Object.keys(recipe).some((key) => ![\"placeholder\", \"preview\"].includes(key))\n )\n throw new Error(\"Unknown recipe field\")\n const placeholder = recipe.placeholder\n if (\n placeholder !== undefined &&\n placeholder !== \"none\" &&\n placeholder !== \"blur\"\n ) {\n if (\n !placeholder ||\n typeof placeholder !== \"object\" ||\n Object.keys(placeholder).some(\n (key) => ![\"blur\", \"width\", \"quality\"].includes(key)\n ) ||\n (placeholder.blur !== undefined &&\n (!Number.isFinite(placeholder.blur) || placeholder.blur < 0)) ||\n (placeholder.width !== undefined &&\n (!Number.isSafeInteger(placeholder.width) || placeholder.width < 1)) ||\n (placeholder.quality !== undefined &&\n (!Number.isInteger(placeholder.quality) ||\n placeholder.quality < 1 ||\n placeholder.quality > 100))\n )\n throw new Error(\"Invalid placeholder settings\")\n }\n const preview = recipe.preview\n if (\n preview !== undefined &&\n preview !== \"none\" &&\n (!preview ||\n typeof preview !== \"object\" ||\n Object.keys(preview).some(\n (key) => ![\"url\", \"maxWidth\", \"ratio\", \"quality\", \"loadFirst\", \"minDisplayMs\"].includes(key)\n ) ||\n (preview.maxWidth !== undefined && !positive(preview.maxWidth)) ||\n (preview.quality !== undefined &&\n (!Number.isInteger(preview.quality) || preview.quality < 1 || preview.quality > 100)) ||\n (preview.ratio !== undefined &&\n (!positive(preview.ratio) || preview.ratio >= 1)) ||\n (preview.url !== undefined && typeof preview.url !== \"string\") ||\n (preview.loadFirst !== undefined && typeof preview.loadFirst !== \"boolean\") ||\n (preview.minDisplayMs !== undefined &&\n (!Number.isFinite(preview.minDisplayMs) || preview.minDisplayMs < 0)))\n )\n throw new Error(\"Invalid preview\")\n}\n","import type { IrisCatalogAsset, IrisRecipe } from \"./policy-types\"\n\nexport function placeholderSettings(input: IrisRecipe[\"placeholder\"]) {\n if (!input || input === \"none\") return null\n const options = input === \"blur\" ? {} : input\n return {\n width: options.width ?? 10,\n quality: options.quality ?? 40,\n blur: options.blur ?? 12,\n }\n}\n\nexport function resolvePlaceholder({\n asset,\n placeholder,\n}: {\n asset: IrisCatalogAsset\n placeholder: IrisRecipe[\"placeholder\"]\n}) {\n const settings = placeholderSettings(placeholder)\n if (!settings) return null\n const url =\n asset.placeholders?.find(\n (item) =>\n item.width === settings.width && item.quality === settings.quality\n )?.url ??\n (settings.width === 10 && settings.quality === 40\n ? asset.placeholder\n : undefined)\n return url ? { url, blur: settings.blur } : null\n}\n"],"mappings":"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","names":["selection","key","value"]}