@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/LICENSE +15 -0
- package/README.md +137 -2
- package/dist/browser.cjs +466 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +69 -0
- package/dist/browser.d.ts +69 -0
- package/dist/browser.js +439 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.cjs +681 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +658 -0
- package/dist/cli.js.map +1 -0
- package/dist/core.cjs +355 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +8 -0
- package/dist/core.d.ts +8 -0
- package/dist/core.js +328 -0
- package/dist/core.js.map +1 -0
- package/dist/next.cjs +380 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +16 -0
- package/dist/next.d.ts +16 -0
- package/dist/next.js +352 -0
- package/dist/next.js.map +1 -0
- package/dist/node.cjs +580 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +47 -0
- package/dist/node.d.ts +47 -0
- package/dist/node.js +542 -0
- package/dist/node.js.map +1 -0
- package/dist/policy-types-LoD2Ldcl.d.cts +147 -0
- package/dist/policy-types-LoD2Ldcl.d.ts +147 -0
- package/dist/react.cjs +628 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +42 -0
- package/dist/react.d.ts +42 -0
- package/dist/react.js +611 -0
- package/dist/react.js.map +1 -0
- package/dist/types-Dv3DS4OX.d.cts +17 -0
- package/dist/types-Dv3DS4OX.d.ts +17 -0
- package/package.json +102 -4
- package/skills/iris-audit/SKILL.md +77 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../packages/cli/index.ts","../packages/node/generate.ts","../packages/node/discover.ts","../packages/node/prepare.ts","../packages/core/policy.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { access, writeFile } from \"node:fs/promises\"\nimport path from \"node:path\"\nimport { createJiti } from \"jiti\"\nimport type { IrisPolicy } from \"../core\"\nimport type { IrisGenerateOptions } from \"../node\"\nimport { generateIris } from \"../node\"\n\nconst args = process.argv.slice(2)\n\nvoid main().catch((error) => {\n console.error(error)\n process.exitCode = 1\n})\n\nasync function main() {\n const command = args.find((argument) => !argument.startsWith(\"-\"))\n if (\n !command ||\n command === \"help\" ||\n args.includes(\"--help\") ||\n args.includes(\"-h\")\n ) {\n printHelp()\n } else if (command === \"init\") {\n await runInit()\n } else if (command === \"generate\") {\n await runGenerate()\n } else {\n throw new Error(`Unknown Iris command: ${command}`)\n }\n}\n\nasync function runInit() {\n const rootDir = process.cwd()\n const existing = await findExistingConfig(rootDir)\n if (existing) {\n console.log(`${path.basename(existing)} already exists; left unchanged.`)\n return\n }\n\n await writeFile(path.join(rootDir, \"iris.config.ts\"), initialConfig, {\n flag: \"wx\",\n })\n console.log(\"Created iris.config.ts.\")\n console.log(\n \"Review sources and scan in iris.config.ts, then run iris generate.\"\n )\n}\n\nconst initialConfig = `import { createIris } from \"@joycostudio/iris\"\n\nexport default createIris({\n assets: [\"public/**/*.{png,jpg,jpeg,webp,avif,gif}\"],\n recipes: {\n direct: { preview: \"none\" },\n blur: { placeholder: \"blur\" },\n },\n rules: [{ when: { priority: true }, recipe: \"direct\" }],\n fallback: \"blur\",\n})\n\nexport const generate = { output: \"src/generated/iris\" }\n`\n\nasync function runGenerate() {\n const rootDir = process.cwd()\n const configArgument = optionValue(\"--config\")\n const configPath = configArgument\n ? path.resolve(rootDir, configArgument)\n : await findConfig(rootDir)\n const jiti = createJiti(process.cwd(), { interopDefault: false })\n const imported = (await jiti.import(configPath)) as {\n default?: IrisPolicy\n generate?:\n | IrisGenerateOptions\n | (() => IrisGenerateOptions | Promise<IrisGenerateOptions>)\n }\n const iris = imported.default\n if (\n !iris ||\n typeof iris.select !== \"function\" ||\n typeof iris.resolve !== \"function\"\n )\n throw new Error(\n `${path.relative(rootDir, configPath)} must default-export createIris(...)`\n )\n const generation =\n typeof imported.generate === \"function\"\n ? await imported.generate()\n : (imported.generate ?? { output: \"src/generated/iris\" })\n const result = await generateIris({\n iris,\n generation,\n rootDir,\n configFile: configPath,\n allowStale: args.includes(\"--allow-stale\"),\n })\n if (result.stale) console.log(\"Iris kept existing generated files.\")\n else\n console.log(\n `Iris prepared ${result.inspected} assets and ${result.placeholders} placeholders; skipped ${result.skipped} optional sources.`\n )\n}\n\nasync function findConfig(rootDir: string) {\n const existing = await findExistingConfig(rootDir)\n if (existing) return existing\n throw new Error(\n \"Iris could not find iris.config.ts (or .mts/.js/.mjs). Run iris init first.\"\n )\n}\n\nasync function findExistingConfig(rootDir: string) {\n for (const name of [\n \"iris.config.ts\",\n \"iris.config.mts\",\n \"iris.config.js\",\n \"iris.config.mjs\",\n ]) {\n const candidate = path.join(rootDir, name)\n try {\n await access(candidate)\n return candidate\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error\n // Try the next conventional filename.\n }\n }\n return null\n}\n\nfunction optionValue(name: string) {\n const index = args.indexOf(name)\n return index < 0 ? undefined : args[index + 1]\n}\n\nfunction printHelp() {\n console.log(`Iris responsive-image tooling\n\nUsage:\n iris init\n iris generate [--config path] [--allow-stale]\n\nCommands:\n init Create a default iris.config.ts without replacing existing config\n generate Discover sources from iris.config and write image manifests\n`)\n}\n","import { mkdir, readFile, realpath, rename, writeFile } from \"node:fs/promises\"\nimport path from \"node:path\"\nimport sharp from \"sharp\"\nimport type { IrisCatalogAsset, IrisPolicy } from \"../core\"\nimport type { IrisGenerateOptions, IrisGenerateSource } from \"./types\"\nimport { discoverIrisSources } from \"./discover\"\nimport { prepareIrisAssets } from \"./prepare\"\n\nexport type IrisGenerateResult = {\n stale: boolean\n placeholders: number\n inspected: number\n skipped: number\n}\n\nexport async function generateIris<Custom>({\n iris,\n generation,\n rootDir = process.cwd(),\n configFile = \"iris.config.ts\",\n allowStale = false,\n fetch: fetcher = fetch,\n}: {\n iris: IrisPolicy<Custom>\n generation: IrisGenerateOptions\n rootDir?: string\n configFile?: string\n allowStale?: boolean\n fetch?: typeof fetch\n}): Promise<IrisGenerateResult> {\n if (!generation.output?.trim())\n throw new Error(\"Iris requires an output directory\")\n const directory = path.resolve(rootDir, generation.output)\n const publicDir = path.resolve(rootDir, generation.publicDir ?? \"public\")\n const adapter = generation.adapter !== false\n try {\n const sources = await discoverIrisSources(\n {\n ...generation,\n sources: [...iris.assets, ...(generation.sources ?? [])],\n },\n rootDir\n )\n const entries = new Map<string, Exclude<IrisGenerateSource, string>>()\n for (const value of sources) {\n const source = typeof value === \"string\" ? { src: value } : value\n const previous = entries.get(source.src)\n entries.set(source.src, {\n ...previous,\n ...source,\n optional: Boolean(\n previous\n ? \"optional\" in previous &&\n previous.optional &&\n \"optional\" in source &&\n source.optional\n : \"optional\" in source && source.optional\n ),\n })\n }\n const catalog: Record<string, IrisCatalogAsset> = {}\n const placeholders: Record<string, string> = {}\n const queue = [...entries.values()]\n let skipped = 0\n let cursor = 0\n async function worker() {\n while (cursor < queue.length) {\n const source = queue[cursor++]\n // Providers may already supply a complete catalog (e.g. Chopper).\n if (\"width\" in source) {\n const prepared = await prepareIrisAssets({\n iris: { assets: [source] },\n rootDir,\n publicDir,\n fetch: fetcher,\n })\n if (!prepared.ok) throw new Error(prepared.error.message)\n catalog[source.src] = prepared.data[0]\n if (source.placeholder) placeholders[source.src] = source.placeholder\n continue\n }\n let bytes: Buffer\n if (/^https?:\\/\\//i.test(source.src)) {\n const response = await fetcher(source.src, {\n signal: AbortSignal.timeout(30_000),\n })\n if (source.optional && response.status === 404) {\n skipped++\n continue\n }\n if (!response.ok)\n throw new Error(`${response.status} fetching ${source.src}`)\n bytes = Buffer.from(await response.arrayBuffer())\n } else {\n const pathname = decodeURIComponent(\n source.src.split(/[?#]/, 1)[0]\n ).replace(/^\\/+/, \"\")\n const file = path.resolve(publicDir, pathname)\n for (const relative of [\n path.relative(publicDir, file),\n path.relative(await realpath(publicDir), await realpath(file)),\n ]) {\n if (\n relative === \"..\" ||\n relative.startsWith(`..${path.sep}`) ||\n path.isAbsolute(relative)\n )\n throw new Error(\n `Iris local source escapes publicDir: ${source.src}`\n )\n }\n bytes = await readFile(file)\n }\n const meta = await sharp(bytes, { animated: true }).metadata()\n const swapped = (meta.orientation ?? 1) >= 5\n const width = swapped ? meta.height : meta.width\n const height = swapped ? meta.width : (meta.pageHeight ?? meta.height)\n if (!width || !height)\n throw new Error(`Cannot inspect dimensions of ${source.src}`)\n const placeholderOptions = source.placeholderOptions ?? {}\n const format = placeholderOptions.format ?? \"webp\"\n const size = placeholderOptions.width ?? 10\n const quality = placeholderOptions.quality ?? 40\n async function encodePlaceholder({\n width,\n quality,\n }: {\n width: number\n quality: number\n }) {\n const buffer = await sharp(bytes, { page: 0, pages: 1 })\n .rotate()\n .resize({\n width,\n height: width,\n fit: \"inside\",\n withoutEnlargement: true,\n })\n .toFormat(format, { quality })\n .toBuffer()\n return `data:image/${format};base64,${buffer.toString(\"base64\")}`\n }\n const placeholder =\n source.placeholder ??\n (await encodePlaceholder({ width: size, quality }))\n placeholders[source.src] = placeholder\n if (source.placeholderOnly) continue\n const preparedPlaceholders = [...(source.placeholders ?? [])]\n for (const request of iris.placeholderRequests) {\n if (\n preparedPlaceholders.some(\n (item) =>\n item.width === request.width && item.quality === request.quality\n )\n )\n continue\n const url =\n request.width === size && request.quality === quality\n ? placeholder\n : await encodePlaceholder(request)\n preparedPlaceholders.push({ ...request, url })\n }\n const animated = (meta.pages ?? 1) > 1\n const alpha = Boolean(\n meta.hasAlpha && (animated || !(await sharp(bytes).stats()).isOpaque)\n )\n const metadata: IrisCatalogAsset = {\n src: source.src,\n id: source.id,\n width,\n height,\n bytes: bytes.length,\n format: meta.format,\n alpha,\n animated,\n progressive: meta.isProgressive ?? false,\n placeholder,\n placeholders: preparedPlaceholders,\n variants: [],\n }\n if (source.variants?.length) {\n const prepared = await prepareIrisAssets({\n iris: {\n assets: [\n { ...metadata, variants: [] },\n ...source.variants.filter(\n (item): item is string => typeof item === \"string\"\n ),\n ],\n },\n rootDir,\n publicDir,\n fetch: fetcher,\n })\n if (!prepared.ok) throw new Error(prepared.error.message)\n metadata.variants = [\n ...metadata.variants!,\n ...source.variants.map((variant) => {\n if (typeof variant !== \"string\") return variant\n const asset = prepared.data.find((item) => item.src === variant)!\n return { ...asset, url: asset.src }\n }),\n ]\n }\n catalog[source.src] = metadata\n }\n }\n const concurrency = generation.concurrency ?? 4\n if (!Number.isSafeInteger(concurrency) || concurrency < 1)\n throw new Error(\"concurrency must be a positive integer\")\n await Promise.all(Array.from({ length: concurrency }, worker))\n const sorted = (data: object) =>\n Object.fromEntries(\n Object.entries(data).sort(([a], [b]) => a.localeCompare(b))\n )\n const files: [string, unknown][] = [\n [\"catalog.json\", sorted(catalog)],\n [\"placeholders.json\", sorted(placeholders)],\n ]\n if (generation.report)\n files.push([\n \"report.json\",\n {\n inspected: Object.keys(catalog).length,\n skipped,\n assets: Object.values(catalog).map((asset) => ({\n src: asset.src,\n result: iris.resolve({ asset }),\n })),\n },\n ])\n await mkdir(directory, { recursive: true })\n for (const [name, data] of files)\n await atomicWrite(\n path.join(directory, name),\n JSON.stringify(data, null, 2) + \"\\n\"\n )\n if (adapter) {\n let configImport = path\n .relative(directory, path.resolve(rootDir, configFile))\n .split(path.sep)\n .join(\"/\")\n .replace(/\\.[cm]?[jt]s$/, \"\")\n if (!configImport.startsWith(\".\")) configImport = `./${configImport}`\n await atomicWrite(\n path.join(directory, \"image.tsx\"),\n `\"use client\"\\n\\n// Generated by Iris. Do not edit.\\nimport { createIrisNextImage } from \"@joycostudio/iris/next\"\\nimport iris from ${JSON.stringify(configImport)}\\nimport catalog from \"./catalog.json\"\\n\\nexport const IrisImage = createIrisNextImage({ iris, catalog })\\n`\n )\n }\n return {\n stale: false,\n placeholders: Object.keys(placeholders).length,\n inspected: Object.keys(catalog).length,\n skipped,\n }\n } catch (error) {\n if (allowStale && (await usableOutput({ directory, adapter }))) {\n console.warn(\"Iris generation failed; keeping existing files:\", error)\n return { stale: true, placeholders: 0, inspected: 0, skipped: 0 }\n }\n throw error\n }\n}\n\nasync function atomicWrite(file: string, content: string) {\n await writeFile(`${file}.tmp`, content)\n await rename(`${file}.tmp`, file)\n}\nasync function usableOutput({\n directory,\n adapter,\n}: {\n directory: string\n adapter: boolean\n}) {\n try {\n const catalog: Record<string, IrisCatalogAsset> = JSON.parse(\n await readFile(path.join(directory, \"catalog.json\"), \"utf8\")\n )\n const placeholders = JSON.parse(\n await readFile(path.join(directory, \"placeholders.json\"), \"utf8\")\n )\n if (\n !catalog ||\n Array.isArray(catalog) ||\n !Object.keys(catalog).length ||\n !placeholders ||\n Array.isArray(placeholders)\n )\n return false\n if (\n adapter &&\n !(await readFile(path.join(directory, \"image.tsx\"), \"utf8\")).includes(\n \"createIrisNextImage\"\n )\n )\n return false\n return true\n } catch {\n return false\n }\n}\n","import { readFile } from \"node:fs/promises\"\nimport path from \"node:path\"\nimport { parse } from \"@babel/parser\"\nimport fg from \"fast-glob\"\nimport { minimatch } from \"minimatch\"\nimport { type IrisGenerateOptions, type IrisGenerateSource } from \"./types\"\n\nconst ignored = [\n \"**/node_modules/**\",\n \"**/.next/**\",\n \"**/.git/**\",\n \"**/dist/**\",\n \"**/coverage/**\",\n \"**/generated/**\",\n \"**/*.generated.*\",\n]\nconst codeFile = /\\.(?:[cm]?[jt]s|[jt]sx)$/i\nconst imageFile = /\\.(?:png|jpe?g|webp|avif|gif)$/i\n\n/** Resolve local globs and literal image strings without executing consumer code. */\nexport async function discoverIrisSources(\n options: IrisGenerateOptions,\n rootDir: string\n): Promise<IrisGenerateSource[]> {\n const publicDir = path.resolve(rootDir, options.publicDir ?? \"public\")\n const exclude = options.exclude ?? []\n const outputDir = path.resolve(rootDir, options.output)\n const explicit: IrisGenerateSource[] = []\n const patterns: string[] = []\n const found = new Set<string>()\n for (const source of options.sources ?? []) {\n if (\n typeof source === \"string\" &&\n !/^(?:https?:\\/\\/|\\/)/i.test(source) &&\n fg.isDynamicPattern(source)\n )\n patterns.push(source)\n else explicit.push(source)\n }\n const globOptions = {\n cwd: rootDir,\n onlyFiles: true,\n followSymbolicLinks: false,\n ignore: [...ignored, ...exclude],\n }\n for (const file of await fg(patterns, globOptions)) {\n if (!imageFile.test(file)) continue\n const relative = path.relative(publicDir, path.resolve(rootDir, file))\n if (\n relative === \"..\" ||\n relative.startsWith(`..${path.sep}`) ||\n path.isAbsolute(relative)\n ) {\n throw new Error(\n `Iris source pattern matched ${file} outside publicDir. Image globs must stay inside ${publicDir}.`\n )\n }\n found.add(`/${relative.split(path.sep).map(encodeURIComponent).join(\"/\")}`)\n }\n for (const file of (await fg(options.scan ?? [], globOptions)).sort()) {\n if (\n !codeFile.test(file) ||\n /\\.d\\.[cm]?ts$/i.test(file) ||\n path.resolve(rootDir, file).startsWith(outputDir + path.sep)\n )\n continue\n const code = await readFile(path.resolve(rootDir, file), \"utf8\")\n try {\n for (const source of scanImageLiterals(code, file)) found.add(source)\n } catch (error) {\n throw new Error(\n `Iris could not scan ${file}: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n }\n const explicitUrls = new Set(\n explicit.map((source) => (typeof source === \"string\" ? source : source.src))\n )\n const excluded = (src: string) =>\n exclude.some(\n (pattern) =>\n src === pattern ||\n minimatch(src, pattern, { dot: true, nonegate: true, nocomment: true })\n )\n return [\n ...[...found]\n .sort()\n .filter((src) => !explicitUrls.has(src) && !excluded(src)),\n ...explicit.filter(\n (source) => !excluded(typeof source === \"string\" ? source : source.src)\n ),\n ]\n}\n\n/** Read literals, not comments, JSX text, interpolated templates or concatenations. */\nexport function scanImageLiterals(code: string, filename: string): string[] {\n const ast = parse(code, {\n sourceType: \"unambiguous\",\n sourceFilename: filename,\n attachComment: false,\n plugins: [\n ...(/\\.[cm]?tsx?$/i.test(filename) ? [\"typescript\" as const] : []),\n ...(/\\.[jt]sx$|\\.[cm]?js$/i.test(filename) ? [\"jsx\" as const] : []),\n ],\n })\n const literals: string[] = []\n function walk(value: unknown) {\n if (!value || typeof value !== \"object\") return\n if (Array.isArray(value)) {\n value.forEach(walk)\n return\n }\n const node = value as Record<string, unknown>\n if (typeof node.type !== \"string\") return\n if (\n node.type.startsWith(\"Comment\") ||\n node.type === \"BinaryExpression\" ||\n node.type === \"TaggedTemplateExpression\" ||\n node.type === \"ImportDeclaration\" ||\n node.type === \"ExportAllDeclaration\" ||\n node.type === \"TSLiteralType\"\n )\n return\n if (node.type === \"StringLiteral\") {\n if (typeof node.value === \"string\") literals.push(node.value)\n return\n }\n if (node.type === \"TemplateLiteral\") {\n const template = node as unknown as {\n expressions: unknown[]\n quasis: { value: { cooked: string | null } }[]\n }\n if (template.expressions.length === 0 && template.quasis[0]?.value.cooked)\n literals.push(template.quasis[0].value.cooked)\n return\n }\n for (const [key, child] of Object.entries(node)) {\n if (\n ![\n \"comments\",\n \"leadingComments\",\n \"trailingComments\",\n \"innerComments\",\n \"loc\",\n \"extra\",\n ].includes(key)\n )\n walk(child)\n }\n }\n walk(ast.program)\n return [\n ...new Set(\n literals.filter((value) =>\n /^(?:https?:\\/\\/|\\/).+\\.(?:png|jpe?g|webp|avif|gif)(?:[?#].*)?$/i.test(\n value\n )\n )\n ),\n ].sort()\n}\n","import { readFile, realpath } from \"node:fs/promises\"\nimport path from \"node:path\"\nimport fg from \"fast-glob\"\nimport sharp from \"sharp\"\nimport type {\n IrisAssetInput,\n IrisCatalogAsset,\n IrisPolicy,\n IrisResult,\n IrisVariant,\n} from \"../core\"\nimport { irisFailure, validVariant } from \"../core/policy\"\n\n/** Discover and inspect only. This adapter does not encode or invent variants. */\nexport async function prepareIrisAssets<Custom>({\n iris,\n rootDir = process.cwd(),\n publicDir = \"public\",\n fetch: fetcher = fetch,\n}: {\n iris: Pick<IrisPolicy<Custom>, \"assets\">\n rootDir?: string\n publicDir?: string\n fetch?: typeof fetch\n}): Promise<IrisResult<IrisCatalogAsset[]>> {\n const publicRoot = path.resolve(rootDir, publicDir)\n try {\n const entries = new Map<string, IrisAssetInput | IrisCatalogAsset>()\n const explicit: (IrisAssetInput | IrisCatalogAsset)[] = []\n for (const input of iris.assets) {\n const src = typeof input === \"string\" ? input : input.src\n if (/^https?:\\/\\//i.test(src)) {\n if (fg.isDynamicPattern(new URL(src).pathname))\n return irisFailure(\n \"remote_pattern\",\n \"Remote URLs must be explicit; a glob cannot enumerate a bucket\"\n )\n explicit.push(input)\n } else if (\n typeof input === \"string\" &&\n fg.isDynamicPattern(src.startsWith(\"/\") ? src.split(/[?#]/, 1)[0] : src)\n ) {\n const files = await fg(\n src.startsWith(\"/\") ? src.split(/[?#]/, 1)[0].slice(1) : src,\n {\n cwd: src.startsWith(\"/\") ? publicRoot : rootDir,\n onlyFiles: true,\n followSymbolicLinks: false,\n ignore: [\"**/node_modules/**\", \"**/.git/**\", \"**/.next/**\"],\n }\n )\n for (const file of files.sort()) {\n const full = path.resolve(\n src.startsWith(\"/\") ? publicRoot : rootDir,\n file\n )\n const relative = within(publicRoot, full)\n const url = `/${relative.split(path.sep).map(encodeURIComponent).join(\"/\")}`\n entries.set(url, url)\n }\n } else explicit.push(input)\n }\n for (const input of explicit) {\n const src = typeof input === \"string\" ? input : input.src\n entries.set(src, input)\n }\n const inspected = new Map<string, Promise<IrisVariant>>()\n function inspect(src: string) {\n let pending = inspected.get(src)\n if (!pending) {\n pending = (async () => {\n let bytes: Buffer\n if (/^https?:\\/\\//i.test(src)) {\n const response = await fetcher(src, {\n signal: AbortSignal.timeout(30_000),\n })\n if (!response.ok)\n throw new Error(`Cannot inspect ${src}: HTTP ${response.status}`)\n bytes = Buffer.from(await response.arrayBuffer())\n } else {\n const pathname = decodeURIComponent(\n src.split(/[?#]/, 1)[0]\n ).replace(/^\\/+/, \"\")\n const file = path.resolve(publicRoot, pathname)\n within(publicRoot, file)\n within(await realpath(publicRoot), await realpath(file))\n bytes = await readFile(file)\n }\n const meta = await sharp(bytes, { animated: true }).metadata()\n const swapped = (meta.orientation ?? 1) >= 5\n const width = swapped ? meta.height : meta.width\n const height = swapped ? meta.width : (meta.pageHeight ?? meta.height)\n if (!width || !height)\n throw new Error(`Cannot inspect dimensions of ${src}`)\n return {\n url: src,\n width,\n height,\n bytes: bytes.length,\n format: meta.format,\n progressive: meta.isProgressive ?? false,\n alpha: Boolean(\n meta.hasAlpha &&\n ((meta.pages ?? 1) > 1 || !(await sharp(bytes).stats()).isOpaque)\n ),\n animated: (meta.pages ?? 1) > 1,\n }\n })()\n inspected.set(src, pending)\n }\n return pending\n }\n const catalog: IrisCatalogAsset[] = []\n const identities = new Set<string>()\n for (const input of entries.values()) {\n const entry = typeof input === \"string\" ? { src: input } : input\n const metadata =\n \"width\" in entry\n ? (entry as IrisCatalogAsset)\n : await inspect(entry.src)\n const { width, height, bytes, format, progressive, alpha, animated } =\n metadata\n const variants = await Promise.all(\n (entry.variants ?? []).map((variant) =>\n typeof variant === \"string\" ? inspect(variant) : variant\n )\n )\n if (\n !validVariant({ url: entry.src, width, height, bytes }) ||\n !variants.every(validVariant)\n )\n return irisFailure(\"invalid_asset\", `Invalid metadata for ${entry.src}`)\n for (const identity of new Set([entry.src, entry.id ?? entry.src])) {\n if (identities.has(identity))\n return irisFailure(\n \"duplicate_asset\",\n `Ambiguous asset identity: ${identity}`\n )\n identities.add(identity)\n }\n catalog.push({\n src: entry.src,\n id: entry.id,\n width,\n height,\n bytes,\n format,\n progressive,\n alpha,\n animated,\n variants,\n placeholder: entry.placeholder,\n placeholders: entry.placeholders,\n })\n }\n return { ok: true, data: catalog }\n } catch (error) {\n return irisFailure(\n \"prepare_failed\",\n error instanceof Error ? error.message : String(error)\n )\n }\n}\n\nfunction within(root: string, file: string) {\n const relative = path.relative(root, file)\n if (\n relative === \"..\" ||\n relative.startsWith(`..${path.sep}`) ||\n path.isAbsolute(relative)\n ) {\n throw new Error(`Image source escapes publicDir: ${file}`)\n }\n return relative\n}\n","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"],"mappings":"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","names":["writeFile","path","readFile","realpath","path","sharp","readFile","path","fg","minimatch","path","inspect","readFile","fg","path","realpath","readFile","sharp","width","quality","path","writeFile"]}
|
package/dist/core.cjs
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// packages/core/index.ts
|
|
21
|
+
var core_exports = {};
|
|
22
|
+
__export(core_exports, {
|
|
23
|
+
createIris: () => createIris
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(core_exports);
|
|
26
|
+
|
|
27
|
+
// packages/core/policy.ts
|
|
28
|
+
var import_minimatch = require("minimatch");
|
|
29
|
+
|
|
30
|
+
// packages/core/placeholder.ts
|
|
31
|
+
function placeholderSettings(input) {
|
|
32
|
+
if (!input || input === "none") return null;
|
|
33
|
+
const options = input === "blur" ? {} : input;
|
|
34
|
+
return {
|
|
35
|
+
width: options.width ?? 10,
|
|
36
|
+
quality: options.quality ?? 40,
|
|
37
|
+
blur: options.blur ?? 12
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function resolvePlaceholder({
|
|
41
|
+
asset,
|
|
42
|
+
placeholder
|
|
43
|
+
}) {
|
|
44
|
+
const settings = placeholderSettings(placeholder);
|
|
45
|
+
if (!settings) return null;
|
|
46
|
+
const url = asset.placeholders?.find(
|
|
47
|
+
(item) => item.width === settings.width && item.quality === settings.quality
|
|
48
|
+
)?.url ?? (settings.width === 10 && settings.quality === 40 ? asset.placeholder : void 0);
|
|
49
|
+
return url ? { url, blur: settings.blur } : null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// packages/core/policy.ts
|
|
53
|
+
function irisFailure(code, message) {
|
|
54
|
+
return { ok: false, error: { code, message } };
|
|
55
|
+
}
|
|
56
|
+
function createIris(options) {
|
|
57
|
+
const recipes = { ...options.recipes };
|
|
58
|
+
const rules = [...options.rules ?? []];
|
|
59
|
+
const assets = [...options.assets ?? []];
|
|
60
|
+
for (const name of [options.fallback, ...rules.map((rule) => rule.recipe)]) {
|
|
61
|
+
if (!Object.hasOwn(recipes, name))
|
|
62
|
+
throw new Error(`Unknown Iris recipe: ${name}`);
|
|
63
|
+
}
|
|
64
|
+
for (const recipe of Object.values(recipes)) {
|
|
65
|
+
if (typeof recipe !== "function") validateRecipe(recipe);
|
|
66
|
+
}
|
|
67
|
+
for (const rule of rules) {
|
|
68
|
+
if (rule.when && typeof rule.when !== "function")
|
|
69
|
+
validateConditions(rule.when);
|
|
70
|
+
}
|
|
71
|
+
function selectRecipe(context, override) {
|
|
72
|
+
let ruleIndex = null;
|
|
73
|
+
let name = override ?? options.fallback;
|
|
74
|
+
try {
|
|
75
|
+
if (override === void 0) {
|
|
76
|
+
const index = rules.findIndex(
|
|
77
|
+
(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)))
|
|
78
|
+
);
|
|
79
|
+
if (index !== -1) {
|
|
80
|
+
ruleIndex = index;
|
|
81
|
+
name = rules[index].recipe;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (!Object.hasOwn(recipes, name))
|
|
85
|
+
return irisFailure("unknown_recipe", `Unknown Iris recipe: ${name}`);
|
|
86
|
+
const registered = recipes[name];
|
|
87
|
+
const recipe = typeof registered === "function" ? registered(context) : registered;
|
|
88
|
+
validateRecipe(recipe);
|
|
89
|
+
return { ok: true, data: { recipe, name, ruleIndex } };
|
|
90
|
+
} catch (error) {
|
|
91
|
+
return irisFailure(
|
|
92
|
+
"invalid_policy",
|
|
93
|
+
error instanceof Error ? error.message : String(error)
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
assets,
|
|
99
|
+
placeholderRequests: [
|
|
100
|
+
...new Map(
|
|
101
|
+
[
|
|
102
|
+
placeholderSettings("blur"),
|
|
103
|
+
...Object.values(recipes).flatMap((recipe) => {
|
|
104
|
+
const settings = typeof recipe === "function" ? null : placeholderSettings(recipe.placeholder);
|
|
105
|
+
return settings ? [settings] : [];
|
|
106
|
+
})
|
|
107
|
+
].map(({ width, quality }) => [
|
|
108
|
+
`${width}:${quality}`,
|
|
109
|
+
{ width, quality }
|
|
110
|
+
])
|
|
111
|
+
).values()
|
|
112
|
+
],
|
|
113
|
+
select({ asset, usage = {}, recipe }) {
|
|
114
|
+
if (!validVariant({ ...asset, url: asset.src }))
|
|
115
|
+
return irisFailure("invalid_asset", "Invalid asset metadata");
|
|
116
|
+
if (usage.width !== void 0 && !positive(usage.width) || usage.dpr !== void 0 && !positive(usage.dpr))
|
|
117
|
+
return irisFailure(
|
|
118
|
+
"invalid_usage",
|
|
119
|
+
"Usage width and DPR must be positive finite numbers"
|
|
120
|
+
);
|
|
121
|
+
return selectRecipe(
|
|
122
|
+
{
|
|
123
|
+
src: asset.src,
|
|
124
|
+
asset,
|
|
125
|
+
variants: [...asset.variants ?? [], { ...asset, url: asset.src }],
|
|
126
|
+
usage
|
|
127
|
+
},
|
|
128
|
+
recipe
|
|
129
|
+
);
|
|
130
|
+
},
|
|
131
|
+
resolve({ asset: input, catalog, usage = {}, recipe: override }) {
|
|
132
|
+
const asset = typeof input === "string" ? [...catalog ?? [], ...assets].find(
|
|
133
|
+
(item) => typeof item !== "string" && (item.src === input || item.id === input) && "width" in item
|
|
134
|
+
) : input;
|
|
135
|
+
if (!asset)
|
|
136
|
+
return irisFailure(
|
|
137
|
+
"asset_unprepared",
|
|
138
|
+
`Prepare or supply metadata for ${String(input)} before resolving`
|
|
139
|
+
);
|
|
140
|
+
if (!validVariant({ ...asset, url: asset.src }))
|
|
141
|
+
return irisFailure(
|
|
142
|
+
"invalid_asset",
|
|
143
|
+
"Asset metadata must contain a URL, positive dimensions and non-negative bytes"
|
|
144
|
+
);
|
|
145
|
+
if (usage.width !== void 0 && !positive(usage.width) || usage.dpr !== void 0 && !positive(usage.dpr)) {
|
|
146
|
+
return irisFailure(
|
|
147
|
+
"invalid_usage",
|
|
148
|
+
"Usage width and DPR must be positive finite numbers"
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const original = {
|
|
152
|
+
url: asset.src,
|
|
153
|
+
width: asset.width,
|
|
154
|
+
height: asset.height,
|
|
155
|
+
bytes: asset.bytes,
|
|
156
|
+
format: asset.format,
|
|
157
|
+
progressive: asset.progressive,
|
|
158
|
+
alpha: asset.alpha,
|
|
159
|
+
animated: asset.animated
|
|
160
|
+
};
|
|
161
|
+
const variants = [...asset.variants ?? [], original];
|
|
162
|
+
if (!variants.every(validVariant))
|
|
163
|
+
return irisFailure(
|
|
164
|
+
"invalid_variant",
|
|
165
|
+
"Prepare variant metadata before resolving"
|
|
166
|
+
);
|
|
167
|
+
const unique = [
|
|
168
|
+
...new Map(
|
|
169
|
+
variants.map((variant) => [variant.url, { ...variant }])
|
|
170
|
+
).values()
|
|
171
|
+
];
|
|
172
|
+
const context = {
|
|
173
|
+
src: asset.src,
|
|
174
|
+
asset,
|
|
175
|
+
variants: unique,
|
|
176
|
+
usage
|
|
177
|
+
};
|
|
178
|
+
try {
|
|
179
|
+
const selection = selectRecipe(context, override);
|
|
180
|
+
if (!selection.ok) return selection;
|
|
181
|
+
const { recipe, name, ruleIndex } = selection.data;
|
|
182
|
+
const target = usage.width === void 0 ? null : Math.ceil(usage.width * (usage.dpr ?? 1));
|
|
183
|
+
if (target !== null && !positive(target))
|
|
184
|
+
return irisFailure("invalid_usage", "Target width overflow");
|
|
185
|
+
const diagnostics = [];
|
|
186
|
+
const note = (code, message) => diagnostics.push({ code, message });
|
|
187
|
+
const width = target ?? asset.width;
|
|
188
|
+
if (target === null)
|
|
189
|
+
note(
|
|
190
|
+
"unknown_target",
|
|
191
|
+
"Layout width is unknown; selecting for original width"
|
|
192
|
+
);
|
|
193
|
+
const compatible = unique.filter(
|
|
194
|
+
(item) => (asset.alpha !== true || item.alpha === true) && (asset.animated !== true || item.animated === true)
|
|
195
|
+
);
|
|
196
|
+
const final = choose(compatible, width);
|
|
197
|
+
if (final.width < width)
|
|
198
|
+
note(
|
|
199
|
+
"undersized_final",
|
|
200
|
+
"The selected final is smaller than the requested resolution"
|
|
201
|
+
);
|
|
202
|
+
let preview = null;
|
|
203
|
+
if (recipe.preview && recipe.preview !== "none") {
|
|
204
|
+
const selection2 = recipe.preview;
|
|
205
|
+
const previewWidth = selection2.ratio === void 0 ? selection2.maxWidth ?? (selection2.url ? Infinity : 640) : Math.min(
|
|
206
|
+
Math.floor(width * selection2.ratio),
|
|
207
|
+
selection2.maxWidth ?? Infinity
|
|
208
|
+
);
|
|
209
|
+
const candidates = compatible.filter(
|
|
210
|
+
(item) => item.url !== final.url && item.width <= previewWidth && item.bytes < final.bytes
|
|
211
|
+
);
|
|
212
|
+
preview = recipe.preview.url ? candidates.find(
|
|
213
|
+
(item) => item.url === recipe.preview.url
|
|
214
|
+
) ?? null : candidates.sort(
|
|
215
|
+
(a, b) => b.width - a.width || a.bytes - b.bytes || a.url.localeCompare(b.url)
|
|
216
|
+
)[0] ?? null;
|
|
217
|
+
if (!preview)
|
|
218
|
+
note(
|
|
219
|
+
"missing_preview",
|
|
220
|
+
"No distinct smaller preview exists; delivering final directly"
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
const placeholder = resolvePlaceholder({
|
|
224
|
+
asset,
|
|
225
|
+
placeholder: recipe.placeholder
|
|
226
|
+
});
|
|
227
|
+
if (recipe.placeholder && recipe.placeholder !== "none" && !placeholder)
|
|
228
|
+
note(
|
|
229
|
+
"missing_placeholder",
|
|
230
|
+
"No placeholder supplied; continuing without blur"
|
|
231
|
+
);
|
|
232
|
+
return {
|
|
233
|
+
ok: true,
|
|
234
|
+
data: {
|
|
235
|
+
src: asset.src,
|
|
236
|
+
recipe: name,
|
|
237
|
+
ruleIndex,
|
|
238
|
+
targetWidth: target,
|
|
239
|
+
placeholder: final.progressive ? null : placeholder,
|
|
240
|
+
preview: final.progressive ? null : preview,
|
|
241
|
+
final,
|
|
242
|
+
diagnostics
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
} catch (error) {
|
|
246
|
+
return irisFailure(
|
|
247
|
+
"invalid_policy",
|
|
248
|
+
error instanceof Error ? error.message : String(error)
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
function positive(value) {
|
|
255
|
+
return Number.isFinite(value) && value > 0;
|
|
256
|
+
}
|
|
257
|
+
function validVariant(value) {
|
|
258
|
+
return value && typeof value.url === "string" && !!value.url && positive(value.width) && positive(value.height) && Number.isFinite(value.bytes) && value.bytes >= 0;
|
|
259
|
+
}
|
|
260
|
+
function choose(variants, target) {
|
|
261
|
+
const adequate = variants.filter((item) => item.width >= target);
|
|
262
|
+
if (adequate.length)
|
|
263
|
+
return adequate.sort(
|
|
264
|
+
(a, b) => a.bytes - b.bytes || a.width - b.width || a.url.localeCompare(b.url)
|
|
265
|
+
)[0];
|
|
266
|
+
return [...variants].sort(
|
|
267
|
+
(a, b) => b.width - a.width || a.bytes - b.bytes || a.url.localeCompare(b.url)
|
|
268
|
+
)[0];
|
|
269
|
+
}
|
|
270
|
+
function predicate(value) {
|
|
271
|
+
if (typeof value !== "boolean")
|
|
272
|
+
throw new Error("Iris predicates must return a synchronous boolean");
|
|
273
|
+
return value;
|
|
274
|
+
}
|
|
275
|
+
function matches(input, context) {
|
|
276
|
+
const matchers = Array.isArray(input) ? input : [input];
|
|
277
|
+
return matchers.some((matcher) => {
|
|
278
|
+
if (typeof matcher === "function") return predicate(matcher(context));
|
|
279
|
+
if (matcher instanceof RegExp)
|
|
280
|
+
return new RegExp(matcher.source, matcher.flags).test(context.src);
|
|
281
|
+
if (context.src === matcher) return true;
|
|
282
|
+
return (0, import_minimatch.minimatch)(context.src.split(/[?#]/, 1)[0], matcher, {
|
|
283
|
+
dot: true,
|
|
284
|
+
nonegate: true,
|
|
285
|
+
nocomment: true
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function conditionsMatch(when, context) {
|
|
290
|
+
const { asset, usage } = context;
|
|
291
|
+
const values = {
|
|
292
|
+
sourceBytes: asset.bytes,
|
|
293
|
+
sourceWidth: asset.width,
|
|
294
|
+
renderedWidth: usage.width,
|
|
295
|
+
targetWidth: usage.width === void 0 ? void 0 : usage.width * (usage.dpr ?? 1)
|
|
296
|
+
};
|
|
297
|
+
for (const key of Object.keys(values)) {
|
|
298
|
+
const range = when[key];
|
|
299
|
+
const value = values[key];
|
|
300
|
+
if (range && (value === void 0 || range.min !== void 0 && value < range.min || range.max !== void 0 && value > range.max))
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
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);
|
|
304
|
+
}
|
|
305
|
+
function validateConditions(when) {
|
|
306
|
+
const supported = [
|
|
307
|
+
"sourceBytes",
|
|
308
|
+
"sourceWidth",
|
|
309
|
+
"renderedWidth",
|
|
310
|
+
"targetWidth",
|
|
311
|
+
"alpha",
|
|
312
|
+
"priority",
|
|
313
|
+
"format"
|
|
314
|
+
];
|
|
315
|
+
for (const [key, value] of Object.entries(when)) {
|
|
316
|
+
if (!supported.includes(key))
|
|
317
|
+
throw new Error(`Unknown Iris condition: ${key}`);
|
|
318
|
+
if (key === "alpha" || key === "priority") {
|
|
319
|
+
if (typeof value !== "boolean") throw new Error(`${key} must be boolean`);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (key === "format") {
|
|
323
|
+
if (typeof value !== "string") throw new Error("format must be a string");
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const range = value;
|
|
327
|
+
if (!range || typeof range !== "object" || Object.keys(range).some((key2) => !["min", "max"].includes(key2)) || Object.values(range).some(
|
|
328
|
+
(value2) => !Number.isFinite(value2) || value2 < 0
|
|
329
|
+
) || range.min !== void 0 && range.max !== void 0 && range.min > range.max)
|
|
330
|
+
throw new Error(`Invalid Iris range: ${key}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function validateRecipe(recipe) {
|
|
334
|
+
if (!recipe || typeof recipe !== "object" || "then" in recipe)
|
|
335
|
+
throw new Error("Recipes must return synchronous objects");
|
|
336
|
+
if (Object.keys(recipe).some((key) => !["placeholder", "preview"].includes(key)))
|
|
337
|
+
throw new Error("Unknown recipe field");
|
|
338
|
+
const placeholder = recipe.placeholder;
|
|
339
|
+
if (placeholder !== void 0 && placeholder !== "none" && placeholder !== "blur") {
|
|
340
|
+
if (!placeholder || typeof placeholder !== "object" || Object.keys(placeholder).some(
|
|
341
|
+
(key) => !["blur", "width", "quality"].includes(key)
|
|
342
|
+
) || 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))
|
|
343
|
+
throw new Error("Invalid placeholder settings");
|
|
344
|
+
}
|
|
345
|
+
const preview = recipe.preview;
|
|
346
|
+
if (preview !== void 0 && preview !== "none" && (!preview || typeof preview !== "object" || Object.keys(preview).some(
|
|
347
|
+
(key) => !["url", "maxWidth", "ratio", "quality", "loadFirst", "minDisplayMs"].includes(key)
|
|
348
|
+
) || 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)))
|
|
349
|
+
throw new Error("Invalid preview");
|
|
350
|
+
}
|
|
351
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
352
|
+
0 && (module.exports = {
|
|
353
|
+
createIris
|
|
354
|
+
});
|
|
355
|
+
//# sourceMappingURL=core.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../packages/core/index.ts","../packages/core/policy.ts","../packages/core/placeholder.ts"],"sourcesContent":["export type { IrisAsset, IrisDiagnostic } from \"./types\"\nexport { createIris } from \"./policy\"\nexport type {\n IrisVariant,\n IrisPlaceholderOptions,\n IrisPlaceholderVariant,\n IrisCatalogAsset,\n IrisAssetInput,\n IrisPolicyUsage,\n IrisPolicyContext,\n IrisMatcher,\n IrisRange,\n IrisConditions,\n IrisRecipe,\n IrisPolicyRecipe,\n IrisRule,\n IrisPolicyOptions,\n IrisPolicyDiagnostic,\n IrisResolvedPlan,\n IrisResult,\n IrisResolveOptions,\n IrisPolicy,\n IrisRecipeSelection,\n IrisSelectOptions,\n} from \"./policy-types\"\n","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"]}
|
package/dist/core.d.cts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { I as IrisAsset, a as IrisDiagnostic } from './types-Dv3DS4OX.cjs';
|
|
2
|
+
import { I as IrisPolicyOptions, a as IrisPolicy } from './policy-types-LoD2Ldcl.cjs';
|
|
3
|
+
export { b as IrisAssetInput, c as IrisCatalogAsset, d as IrisConditions, e as IrisMatcher, f as IrisPlaceholderOptions, g as IrisPlaceholderVariant, h as IrisPolicyContext, i as IrisPolicyDiagnostic, j as IrisPolicyRecipe, k as IrisPolicyUsage, l as IrisRange, m as IrisRecipe, n as IrisRecipeSelection, o as IrisResolveOptions, p as IrisResolvedPlan, q as IrisResult, r as IrisRule, s as IrisSelectOptions, t as IrisVariant } from './policy-types-LoD2Ldcl.cjs';
|
|
4
|
+
|
|
5
|
+
/** First matching rule wins. Construction is synchronous and performs no I/O. */
|
|
6
|
+
declare function createIris<Custom = Record<string, unknown>>(options: IrisPolicyOptions<Custom>): IrisPolicy<Custom>;
|
|
7
|
+
|
|
8
|
+
export { IrisPolicy, IrisPolicyOptions, createIris };
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { I as IrisAsset, a as IrisDiagnostic } from './types-Dv3DS4OX.js';
|
|
2
|
+
import { I as IrisPolicyOptions, a as IrisPolicy } from './policy-types-LoD2Ldcl.js';
|
|
3
|
+
export { b as IrisAssetInput, c as IrisCatalogAsset, d as IrisConditions, e as IrisMatcher, f as IrisPlaceholderOptions, g as IrisPlaceholderVariant, h as IrisPolicyContext, i as IrisPolicyDiagnostic, j as IrisPolicyRecipe, k as IrisPolicyUsage, l as IrisRange, m as IrisRecipe, n as IrisRecipeSelection, o as IrisResolveOptions, p as IrisResolvedPlan, q as IrisResult, r as IrisRule, s as IrisSelectOptions, t as IrisVariant } from './policy-types-LoD2Ldcl.js';
|
|
4
|
+
|
|
5
|
+
/** First matching rule wins. Construction is synchronous and performs no I/O. */
|
|
6
|
+
declare function createIris<Custom = Record<string, unknown>>(options: IrisPolicyOptions<Custom>): IrisPolicy<Custom>;
|
|
7
|
+
|
|
8
|
+
export { IrisPolicy, IrisPolicyOptions, createIris };
|