{"_id":"@adzazueta/color-extractor","_rev":"6-8f4e59ecb43c219402af3982051f122a","name":"@adzazueta/color-extractor","dist-tags":{"latest":"0.3.0","next":"0.2.0-next.1"},"versions":{"0.1.0":{"name":"@adzazueta/color-extractor","version":"0.1.0","keywords":["color","extraction","palette","image","cielab","kmeans","typescript","esm"],"author":{"name":"Alexis D. Zazueta","email":"alexisd.zazueta@gmail.com"},"license":"MIT","_id":"@adzazueta/color-extractor@0.1.0","maintainers":[{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"}],"homepage":"https://github.com/adzazueta/color-extractor#readme","bugs":{"url":"https://github.com/adzazueta/color-extractor/issues"},"dist":{"shasum":"6116da270e1f3e5e26293dce58bd81d34afead25","tarball":"https://registry.npmjs.org/@adzazueta/color-extractor/-/color-extractor-0.1.0.tgz","fileCount":15,"integrity":"sha512-Ra0XOv9rIy5OwZ9wxLjZ1hmWoVAP6xkKKNu9KSmoWrS9f6NTE1GqopXM/JIbZ0YiVREfAbjBD8+/oz+5iglUpg==","signatures":[{"sig":"MEUCIQCqUEl+AgnuusRolQv/yYae5Ee0yzPE4jRFcmsEY92V1wIgLBWsJlwNwsvgu6O1WEYHfsiBiNvWh3XuwqVn1yWFKMo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":129645},"type":"module","_from":"file:adzazueta-color-extractor-0.1.0.tgz","engines":{"node":"^20.19.0 || >=22.12.0"},"exports":{".":{"node":"./dist/node/index.js","types":"./dist/index.d.ts","browser":"./dist/browser/index.js","default":"./dist/browser/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./node":{"types":"./dist/node/index.d.ts","import":"./dist/node/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","import":"./dist/browser/index.js"},"./package.json":"./package.json"},"scripts":{"lint":"biome check .","test":"vitest run","build":"tsdown","release":"changeset publish","lint:fix":"biome check --write .","changeset":"changeset","typecheck":"tsc --noEmit","test:smoke":"node scripts/smoke.mjs","test:watch":"vitest","test:verbose":"vitest run --reporter=verbose"},"_npmUser":{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"},"_resolved":"/tmp/9ded639aa98e1b048f9693c2772474e2/adzazueta-color-extractor-0.1.0.tgz","_integrity":"sha512-Ra0XOv9rIy5OwZ9wxLjZ1hmWoVAP6xkKKNu9KSmoWrS9f6NTE1GqopXM/JIbZ0YiVREfAbjBD8+/oz+5iglUpg==","repository":{"url":"git+https://github.com/adzazueta/color-extractor.git","type":"git"},"_npmVersion":"10.9.8","description":"ESM-only npm package for extracting perceptually meaningful primary and secondary colors from images in browser and Node.js","directories":{},"lint-staged":{"*.{js,ts,cjs,mjs}":["biome check --write --no-errors-on-unmatched","vitest related --run --passWithNoTests"]},"sideEffects":false,"_nodeVersion":"22.23.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"sharp":"^0.35.0","unrun":"^0.3.1","tsdown":"^0.22.0","vitest":"^4.1.0","publint":"^0.3.21","typescript":"^6.0.0","@types/node":"^26.1.0","lint-staged":"^17.0.8","@biomejs/biome":"^2.5.4","@changesets/cli":"^2.31.1","simple-git-hooks":"^2.13.1"},"peerDependencies":{"sharp":"^0.33.0"},"simple-git-hooks":{"pre-commit":"pnpm lint-staged && pnpm typecheck"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/color-extractor_0.1.0_1784318216257_0.7079319151373233","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@adzazueta/color-extractor","version":"0.1.1","keywords":["color","extraction","palette","image","cielab","kmeans","typescript","esm"],"author":{"name":"Alexis D. Zazueta","email":"alexisd.zazueta@gmail.com"},"license":"MIT","_id":"@adzazueta/color-extractor@0.1.1","maintainers":[{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"}],"homepage":"https://github.com/adzazueta/color-extractor#readme","bugs":{"url":"https://github.com/adzazueta/color-extractor/issues"},"dist":{"shasum":"d9a27312901932fa17f89a20fd71f9fef81c12c3","tarball":"https://registry.npmjs.org/@adzazueta/color-extractor/-/color-extractor-0.1.1.tgz","fileCount":15,"integrity":"sha512-GjyFbTmSvrNauHVS1pT+UbkBSCB9y15UiPqOmvNEbsTGspUjsQeAXTJYtYo/G53rP6y4gfXEfzmaMpBItTw7VQ==","signatures":[{"sig":"MEYCIQC3dqEo87YxU2sui+IJL2ZKdvNSlg8MawmyzHuJL9qBXAIhAMMCpKmzoeYG0PDZZWL3WwNe+SSPi8te+ASNDY2mhG1c","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":121636},"type":"module","_from":"file:adzazueta-color-extractor-0.1.1.tgz","engines":{"node":"^20.19.0 || >=22.12.0"},"exports":{".":{"node":"./dist/node/index.js","types":"./dist/index.d.ts","browser":"./dist/browser/index.js","default":"./dist/browser/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./node":{"types":"./dist/node/index.d.ts","import":"./dist/node/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","import":"./dist/browser/index.js"},"./package.json":"./package.json"},"scripts":{"lint":"biome check .","test":"vitest run","build":"tsdown","release":"changeset publish","lint:fix":"biome check --write .","changeset":"changeset","typecheck":"tsc --noEmit","test:smoke":"node scripts/smoke.mjs","test:watch":"vitest","test:verbose":"vitest run --reporter=verbose"},"_npmUser":{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"},"_resolved":"/tmp/4818ae8c97eaea0202b593ecaed76803/adzazueta-color-extractor-0.1.1.tgz","_integrity":"sha512-GjyFbTmSvrNauHVS1pT+UbkBSCB9y15UiPqOmvNEbsTGspUjsQeAXTJYtYo/G53rP6y4gfXEfzmaMpBItTw7VQ==","repository":{"url":"git+https://github.com/adzazueta/color-extractor.git","type":"git"},"_npmVersion":"10.9.8","description":"ESM-only npm package for extracting perceptually meaningful primary and secondary colors from images in browser and Node.js","directories":{},"lint-staged":{"*.{js,ts,cjs,mjs}":["biome check --write --no-errors-on-unmatched","vitest related --run --passWithNoTests"]},"sideEffects":false,"_nodeVersion":"22.23.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"sharp":"^0.35.0","unrun":"^0.3.1","tsdown":"^0.22.0","vitest":"^4.1.0","publint":"^0.3.21","typescript":"^6.0.0","@types/node":"^26.1.0","lint-staged":"^17.0.8","@biomejs/biome":"^2.5.4","@changesets/cli":"^2.31.1","simple-git-hooks":"^2.13.1"},"peerDependencies":{"sharp":"^0.33.0"},"simple-git-hooks":{"pre-commit":"pnpm lint-staged && pnpm typecheck"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/color-extractor_0.1.1_1784322316949_0.3040514008381954","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@adzazueta/color-extractor","version":"0.1.2","keywords":["color","extraction","palette","image","cielab","kmeans","typescript","esm"],"author":{"name":"Alexis D. Zazueta","email":"alexisd.zazueta@gmail.com"},"license":"MIT","_id":"@adzazueta/color-extractor@0.1.2","maintainers":[{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"}],"homepage":"https://github.com/adzazueta/color-extractor#readme","bugs":{"url":"https://github.com/adzazueta/color-extractor/issues"},"dist":{"shasum":"5ee2da5c88ca09dfb0ffb93e085f4b45a9c7d5f8","tarball":"https://registry.npmjs.org/@adzazueta/color-extractor/-/color-extractor-0.1.2.tgz","fileCount":17,"integrity":"sha512-1th/DWFDiqQK+b1vZcF7d4KCRqdeSt+Smoa9fR4MoBvkqjJRzXBN8yJo+JZi5fHHeVwk69VbS8pReCm2Eg3d8g==","signatures":[{"sig":"MEYCIQCsMOaXIsJuUkZ3KmgkgFZ5Je2O/WIX2yq6wJmBI0zgrgIhAMk5FJhvkqGpNVgwZNr3W8/FndLd10hRlICAkVtVIqk/","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":124111},"type":"module","_from":"file:adzazueta-color-extractor-0.1.2.tgz","engines":{"node":"^20.19.0 || >=22.12.0"},"exports":{".":{"node":"./dist/node/index.js","types":"./dist/index.d.ts","browser":"./dist/browser/index.js","default":"./dist/browser/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./node":{"types":"./dist/node/index.d.ts","import":"./dist/node/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","import":"./dist/browser/index.js"},"./package.json":"./package.json"},"scripts":{"lint":"biome check .","test":"vitest run","build":"pnpm sync-version && tsdown && node scripts/check-build-warnings.mjs","release":"changeset publish","lint:fix":"biome check --write .","changeset":"changeset","typecheck":"tsc --noEmit","test:smoke":"node scripts/smoke.mjs","test:watch":"vitest","sync-version":"node scripts/sync-version.mjs","test:verbose":"vitest run --reporter=verbose","check-version":"pnpm sync-version && git diff --quiet src/generated/version.ts || (echo 'error: src/generated/version.ts is out of sync — run pnpm sync-version and commit the result'; exit 1)","release:version":"pnpm changeset version && pnpm sync-version && pnpm build","verify-fixtures":"node scripts/verify-fixtures.mjs","check-build-warnings":"node scripts/check-build-warnings.mjs"},"_npmUser":{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"},"_resolved":"/tmp/e91afcc17912c77fad0417a52f5ff808/adzazueta-color-extractor-0.1.2.tgz","_integrity":"sha512-1th/DWFDiqQK+b1vZcF7d4KCRqdeSt+Smoa9fR4MoBvkqjJRzXBN8yJo+JZi5fHHeVwk69VbS8pReCm2Eg3d8g==","repository":{"url":"git+https://github.com/adzazueta/color-extractor.git","type":"git"},"_npmVersion":"10.9.8","description":"Extract perceptually meaningful observed colors and palette data from images in browsers and Node.js","directories":{},"lint-staged":{"*.{js,ts,cjs,mjs}":["biome check --write --no-errors-on-unmatched","vitest related --run --passWithNoTests"]},"sideEffects":false,"_nodeVersion":"22.23.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"sharp":"^0.35.0","unrun":"^0.3.1","tsdown":"^0.22.0","vitest":"^4.1.0","publint":"^0.3.21","typescript":"^6.0.0","@types/node":"^26.1.0","lint-staged":"^17.0.8","@biomejs/biome":"^2.5.4","@changesets/cli":"^2.31.1","simple-git-hooks":"^2.13.1"},"peerDependencies":{"sharp":"^0.33.0"},"simple-git-hooks":{"pre-commit":"pnpm lint-staged && pnpm typecheck"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/color-extractor_0.1.2_1784504487283_0.9918568158542898","host":"s3://npm-registry-packages-npm-production"}},"0.2.0-next.0":{"name":"@adzazueta/color-extractor","version":"0.2.0-next.0","keywords":["color","extraction","palette","image","cielab","kmeans","typescript","esm"],"author":{"name":"Alexis D. Zazueta","email":"alexisd.zazueta@gmail.com"},"license":"MIT","_id":"@adzazueta/color-extractor@0.2.0-next.0","maintainers":[{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"}],"homepage":"https://github.com/adzazueta/color-extractor#readme","bugs":{"url":"https://github.com/adzazueta/color-extractor/issues"},"dist":{"shasum":"54f8396d10ab18950ad803e50f03a4bd90faa96a","tarball":"https://registry.npmjs.org/@adzazueta/color-extractor/-/color-extractor-0.2.0-next.0.tgz","fileCount":18,"integrity":"sha512-zEpqmNIt/Dj/+6HEeaMHbjdVsvr3S6sj1+a588l6Yy8By3Z52K0MJOmHXLrbYSNDGf5zeG3QzAzXCIxsOf9Yyg==","signatures":[{"sig":"MEUCIQD5zn6rusCi7LQK6gHS+Zgv07cdUtneW0oF7V75SP7crQIgP75/vkyBv0s8sr85tprpBJ+iOlXfeOgANPFKBZhWXY4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":210099},"type":"module","_from":"file:adzazueta-color-extractor-0.2.0-next.0.tgz","engines":{"node":"^20.19.0 || >=22.12.0"},"exports":{".":{"node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js"},"browser":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"default":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./node":{"types":"./dist/node/index.d.ts","import":"./dist/node/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","import":"./dist/browser/index.js"},"./package.json":"./package.json"},"scripts":{"lint":"biome check .","test":"vitest run","build":"pnpm sync-version && tsdown && node scripts/check-build-warnings.mjs","release":"changeset publish","lint:fix":"biome check --write .","changeset":"changeset","typecheck":"tsc --noEmit","test:smoke":"node scripts/smoke.mjs","test:watch":"vitest","sync-version":"node scripts/sync-version.mjs","test:verbose":"vitest run --reporter=verbose","check-version":"pnpm sync-version && git diff --quiet src/generated/version.ts || (echo 'error: src/generated/version.ts is out of sync — run pnpm sync-version and commit the result'; exit 1)","release:version":"pnpm changeset version && pnpm sync-version && pnpm build","verify-fixtures":"node scripts/verify-fixtures.mjs","check-build-warnings":"node scripts/check-build-warnings.mjs"},"_npmUser":{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"},"_resolved":"/tmp/c6bc32992123bbac58420895ca5a72e8/adzazueta-color-extractor-0.2.0-next.0.tgz","_integrity":"sha512-zEpqmNIt/Dj/+6HEeaMHbjdVsvr3S6sj1+a588l6Yy8By3Z52K0MJOmHXLrbYSNDGf5zeG3QzAzXCIxsOf9Yyg==","repository":{"url":"git+https://github.com/adzazueta/color-extractor.git","type":"git"},"_npmVersion":"10.9.8","description":"Extract perceptually meaningful observed colors and palette data from images in browsers and Node.js","directories":{},"lint-staged":{"*.{js,ts,cjs,mjs}":["biome check --write --no-errors-on-unmatched","vitest related --run --passWithNoTests"]},"sideEffects":false,"_nodeVersion":"22.23.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"sharp":"^0.35.0","unrun":"^0.3.1","tsdown":"^0.22.0","vitest":"^4.1.0","publint":"^0.3.21","typescript":"^6.0.0","@types/node":"^26.1.0","lint-staged":"^17.0.8","@biomejs/biome":"^2.5.4","@changesets/cli":"^2.31.1","simple-git-hooks":"^2.13.1"},"peerDependencies":{"sharp":"^0.33.0"},"simple-git-hooks":{"pre-commit":"pnpm lint-staged && pnpm typecheck"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/color-extractor_0.2.0-next.0_1784576205543_0.06216102139576818","host":"s3://npm-registry-packages-npm-production"}},"0.2.0-next.1":{"name":"@adzazueta/color-extractor","version":"0.2.0-next.1","keywords":["color","extraction","palette","image","cielab","kmeans","typescript","esm"],"author":{"name":"Alexis D. Zazueta","email":"alexisd.zazueta@gmail.com"},"license":"MIT","_id":"@adzazueta/color-extractor@0.2.0-next.1","maintainers":[{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"}],"homepage":"https://github.com/adzazueta/color-extractor#readme","bugs":{"url":"https://github.com/adzazueta/color-extractor/issues"},"dist":{"shasum":"d9e2c02ae9679fbf858b45c1069e0a25470b5402","tarball":"https://registry.npmjs.org/@adzazueta/color-extractor/-/color-extractor-0.2.0-next.1.tgz","fileCount":18,"integrity":"sha512-NBuw7P61atiLn7+FCitcMY4avfzrghCbGZvzRKJrdZegTpr+0r8CvLvGxSc91ALFXI4gYzpQCqr0a0qnWwzzEQ==","signatures":[{"sig":"MEUCIQDFVCqJmBK554EH2ZWXi4Dq3MTqY6mzqYD7s/p84qdCHAIgCgO+ujsogwDq6Q/so/S4PhsvfVMgTQcFXktI//BB1ts=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":240216},"type":"module","_from":"file:adzazueta-color-extractor-0.2.0-next.1.tgz","engines":{"node":"^20.19.0 || >=22.12.0"},"exports":{".":{"node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js"},"browser":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"default":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./node":{"types":"./dist/node/index.d.ts","import":"./dist/node/index.js"},"./browser":{"types":"./dist/browser/index.d.ts","import":"./dist/browser/index.js"},"./package.json":"./package.json"},"scripts":{"lint":"biome check .","test":"vitest run","build":"pnpm sync-version && tsdown && node scripts/check-build-warnings.mjs","release":"changeset publish","lint:fix":"biome check --write .","changeset":"changeset","typecheck":"tsc --noEmit","test:smoke":"node scripts/smoke.mjs","test:watch":"vitest","sync-version":"node scripts/sync-version.mjs","test:verbose":"vitest run --reporter=verbose","benchmark:run":"tsx scripts/benchmark.mjs run","check-version":"pnpm sync-version && git diff --quiet src/generated/version.ts || (echo 'error: src/generated/version.ts is out of sync — run pnpm sync-version and commit the result'; exit 1)","release:check":"pnpm check-version && pnpm lint && pnpm typecheck && pnpm build && pnpm test && pnpm test:smoke && pnpm verify-fixtures","benchmark:smoke":"tsx scripts/benchmark.mjs smoke","release:prepare":"pnpm changeset version && pnpm sync-version","verify-fixtures":"node scripts/verify-fixtures.mjs","benchmark:corpus":"tsx scripts/benchmark.mjs corpus","benchmark:report":"tsx scripts/benchmark.mjs report","benchmark:compare":"tsx scripts/benchmark.mjs compare","benchmark:baseline":"tsx scripts/benchmark.mjs baseline","check-build-warnings":"node scripts/check-build-warnings.mjs"},"_npmUser":{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"},"_resolved":"/tmp/efd03d67021845c06abea836e90bcfaa/adzazueta-color-extractor-0.2.0-next.1.tgz","_integrity":"sha512-NBuw7P61atiLn7+FCitcMY4avfzrghCbGZvzRKJrdZegTpr+0r8CvLvGxSc91ALFXI4gYzpQCqr0a0qnWwzzEQ==","repository":{"url":"git+https://github.com/adzazueta/color-extractor.git","type":"git"},"_npmVersion":"10.9.0","description":"Extract perceptually meaningful observed colors and palette data from images in browsers and Node.js","directories":{},"lint-staged":{"*.{js,ts,cjs,mjs}":["biome check --write --no-errors-on-unmatched","vitest related --run --passWithNoTests"]},"sideEffects":false,"_nodeVersion":"22.12.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"readmeFilename":"README.md","devDependencies":{"tsx":"^4.19.3","sharp":"^0.35.0","unrun":"^0.3.1","tsdown":"^0.22.0","vitest":"^4.1.0","publint":"^0.3.21","typescript":"^6.0.0","@types/node":"^26.1.0","lint-staged":"^17.0.8","@biomejs/biome":"^2.5.4","@changesets/cli":"^2.31.1","simple-git-hooks":"^2.13.1"},"peerDependencies":{"sharp":"^0.33.0"},"simple-git-hooks":{"pre-commit":"pnpm lint-staged && pnpm typecheck"},"peerDependenciesMeta":{"sharp":{"optional":true}},"onlyBuiltDependencies":["esbuild"],"_npmOperationalInternal":{"tmp":"tmp/color-extractor_0.2.0-next.1_1784860493937_0.30989193588861275","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@adzazueta/color-extractor","version":"0.3.0","description":"Extract perceptually meaningful observed colors from images in browsers and Node.js","type":"module","keywords":["color","extraction","palette","image","cielab","kmeans","typescript","esm"],"author":{"name":"Alexis D. Zazueta","email":"alexisd.zazueta@gmail.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/adzazueta/color-extractor.git"},"bugs":{"url":"https://github.com/adzazueta/color-extractor/issues"},"homepage":"https://github.com/adzazueta/color-extractor#readme","engines":{"node":"^20.19.0 || >=22.12.0"},"publishConfig":{"access":"public"},"exports":{".":{"browser":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"},"node":{"types":"./dist/node/index.d.ts","default":"./dist/node/index.js"},"default":{"types":"./dist/browser/index.d.ts","default":"./dist/browser/index.js"}},"./browser":{"types":"./dist/browser/index.d.ts","import":"./dist/browser/index.js","default":"./dist/browser/index.js"},"./node":{"types":"./dist/node/index.d.ts","import":"./dist/node/index.js","default":"./dist/node/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js","default":"./dist/core/index.js"},"./package.json":"./package.json"},"sideEffects":false,"simple-git-hooks":{"pre-commit":"pnpm lint-staged && pnpm typecheck"},"lint-staged":{"*.{js,ts,cjs,mjs}":["biome check --write --no-errors-on-unmatched","vitest related --run --passWithNoTests"]},"peerDependencies":{"sharp":"^0.35.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"onlyBuiltDependencies":["esbuild"],"devDependencies":{"@biomejs/biome":"^2.5.4","@changesets/cli":"^2.31.1","@types/node":"^26.1.0","lint-staged":"^17.0.8","publint":"^0.3.21","sharp":"^0.35.0","simple-git-hooks":"^2.13.1","tsdown":"^0.22.0","tsx":"^4.19.3","typescript":"^6.0.0","unrun":"^0.3.1","vitest":"^4.1.0"},"scripts":{"build":"pnpm sync-version && tsdown && node scripts/check-build-warnings.mjs && node scripts/strip-declaration-maps.mjs","check-build-warnings":"node scripts/check-build-warnings.mjs","changeset":"changeset","release":"changeset publish","release:prepare":"pnpm changeset version && pnpm sync-version","release:check":"pnpm check-version && pnpm lint && pnpm typecheck && pnpm build && pnpm test && pnpm test:smoke && pnpm verify-fixtures","sync-version":"node scripts/sync-version.mjs","check-version":"node scripts/check-version.mjs","test":"vitest run","test:verbose":"vitest run --reporter=verbose","test:watch":"vitest","test:smoke":"node scripts/smoke.mjs","test:ci":"pnpm run build && pnpm test","verify-fixtures":"node scripts/verify-fixtures.mjs","benchmark:corpus":"tsx scripts/benchmark.mjs corpus","benchmark:smoke":"tsx scripts/benchmark.mjs smoke","benchmark:run":"tsx scripts/benchmark.mjs run","benchmark:baseline":"tsx scripts/benchmark.mjs baseline","benchmark:compare":"tsx scripts/benchmark.mjs compare","benchmark:report":"tsx scripts/benchmark.mjs report","typecheck":"tsc --noEmit","lint":"biome check .","lint:fix":"biome check --write ."},"_id":"@adzazueta/color-extractor@0.3.0","_integrity":"sha512-BF0NyAcKwCqxGGEfD8zWfGK4TBaEEATu98IuwoFN7vLZRp9nROAxgY9PFunARPDR/0z22tVWox9isnwUSFU/+Q==","_resolved":"/tmp/fbb588a0c1b3ef701d2070e858159b9b/adzazueta-color-extractor-0.3.0.tgz","_from":"file:adzazueta-color-extractor-0.3.0.tgz","_nodeVersion":"22.12.0","_npmVersion":"10.9.0","dist":{"integrity":"sha512-BF0NyAcKwCqxGGEfD8zWfGK4TBaEEATu98IuwoFN7vLZRp9nROAxgY9PFunARPDR/0z22tVWox9isnwUSFU/+Q==","shasum":"70e5fd0c031df27ad6f18b3843c472174a087d0d","tarball":"https://registry.npmjs.org/@adzazueta/color-extractor/-/color-extractor-0.3.0.tgz","fileCount":22,"unpackedSize":458681,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDfL91O94V6rCGirR+9Lj5IqzzV+/FaYU6iS+C/X+eTBAIgau0fgGyzMfdQdL3nDEZqwd1piMsnZsc6c8r/gEazqFo="}]},"_npmUser":{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"},"directories":{},"maintainers":[{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/color-extractor_0.3.0_1785023774195_0.3772604521312637"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-17T19:56:56.081Z","modified":"2026-07-25T23:56:14.699Z","0.1.0":"2026-07-17T19:56:56.487Z","0.1.1":"2026-07-17T21:05:17.126Z","0.1.2":"2026-07-19T23:41:27.424Z","0.2.0-next.0":"2026-07-20T19:36:45.687Z","0.2.0-next.1":"2026-07-24T02:34:54.102Z","0.3.0":"2026-07-25T23:56:14.559Z"},"bugs":{"url":"https://github.com/adzazueta/color-extractor/issues"},"author":{"name":"Alexis D. Zazueta","email":"alexisd.zazueta@gmail.com"},"license":"MIT","homepage":"https://github.com/adzazueta/color-extractor#readme","keywords":["color","extraction","palette","image","cielab","kmeans","typescript","esm"],"repository":{"type":"git","url":"git+https://github.com/adzazueta/color-extractor.git"},"description":"Extract perceptually meaningful observed colors from images in browsers and Node.js","maintainers":[{"name":"adzazueta","email":"alexisd.zazueta@gmail.com"}],"readme":"# @adzazueta/color-extractor\n\nExtract perceptually meaningful observed colors from images in browsers and Node.js.\n\nThe neutral color extraction API uses deterministic CIELAB K-means by default and also supports deterministic MMCQ quantization. Both algorithms return observed colors, population evidence, perceptual rankings, and algorithm diagnostics without assigning semantic roles.\n\nThe `0.3` release uses a stable color-oriented API that returns observed-color evidence without semantic role assignment.\n\nThis package is ESM-only. Use `import` with the documented package entrypoints.\n\n```ts\nimport { extractColors } from '@adzazueta/color-extractor'\n\nconst result = await extractColors(image)\nconst topId = result.rankings.perceptual[0]\nconst top = result.colors.find(color => color.id === topId)\nconsole.log(top?.id, top?.hex, top?.score)\n```\n\n## Installation\n\nTo install the package:\n\n```sh\nnpm install @adzazueta/color-extractor\n```\n\nFor Node.js image decoding, install the optional `sharp` peer dependency:\n\n```sh\nnpm install sharp\n```\n\nThe Node entrypoint requires Node.js `^20.19.0 || >=22.12.0`. Browser and core consumers do not need `sharp`.\n\n## Quick start\n\n### Universal (root)\n\n```ts\nimport { extractColors } from '@adzazueta/color-extractor'\n\nconst result = await extractColors(image)\n\n// Resolve rankings to colors\nconst colorsById = new Map(\n  result.colors.map(color => [color.id, color]),\n)\nconst perceptual = result.rankings.perceptual.map(\n  id => colorsById.get(id)!,\n)\nconsole.log(perceptual[0]?.hex)\n```\n\n### Browser — file input\n\n```ts\nimport { extractColors } from '@adzazueta/color-extractor'\n\nconst fileInput = document.querySelector<HTMLInputElement>('input[type=\"file\"]')\nconst file = fileInput?.files?.[0]\nif (file) {\n  const result = await extractColors(file)\n  console.log(result.colors[0]?.hex)\n}\n```\n\n### Browser — ImageData\n\n```ts\nimport { extractColorsFromImageData } from '@adzazueta/color-extractor/browser'\n\nconst canvas = document.querySelector('canvas')\nconst ctx = canvas?.getContext('2d')\nif (ctx) {\n  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)\n  const result = await extractColorsFromImageData(imageData)\n  console.log(result.colors)\n}\n```\n\n### Node — local path\n\n```ts\nimport { extractColors } from '@adzazueta/color-extractor/node'\n\nconst result = await extractColors('./photo.jpg')\nconsole.log(result.rankings.perceptual)\n```\n\n### Node — Buffer\n\n```ts\nimport { readFile } from 'node:fs/promises'\nimport { extractColors } from '@adzazueta/color-extractor/node'\n\nconst buffer = await readFile('./photo.jpg')\nconst result = await extractColors(buffer)\nconsole.log(result.metadata.validPixels)\n```\n\n### Core — pixel buffer\n\n```ts\nimport { extractColorsFromPixels } from '@adzazueta/color-extractor/core'\n\nconst result = await extractColorsFromPixels({\n  data: new Uint8Array([/* RGBA bytes: width * height * 4 */]),\n  width: 200,\n  height: 150,\n  channels: 4,\n})\nconsole.log(result.colors.length)\n```\n\n## Public entrypoints\n\n| Import path | Primary functions | Runtime |\n| --- | --- | --- |\n| `@adzazueta/color-extractor` | `extractColors` | Browser or Node through package export conditions |\n| `@adzazueta/color-extractor/browser` | `extractColors`, `extractColorsFromImageData` | Browser |\n| `@adzazueta/color-extractor/node` | `extractColors` | Node.js |\n| `@adzazueta/color-extractor/core` | `extractColorsFromPixels` | Any; no decoder dependencies |\n\nThe root import uses package export conditions: Node resolves the Node entrypoint, browser-oriented resolution uses the Browser entrypoint, and the default condition is Browser. Use an explicit subpath when you need deterministic runtime selection.\n\nAll entrypoints expose the relevant public types and `VERSION`. All entrypoints also expose `ColorExtractorError`, `COLOR_EXTRACTOR_ERROR_CODES`, and `DEFAULT_NEUTRAL_OPTIONS`. Root, browser, and node expose the `ExtractColorOptions` union type covering all runtime-specific option shapes (`BrowserExtractColorOptions | NodeExtractColorOptions`); core exposes `CoreExtractColorOptions`. Generated TypeScript declarations are the complete export reference.\n\nUse explicit subpath imports when you need runtime-specific types, browser decoding helpers, or the Core pixel API.\n\n### Primary signatures\n\n```ts\nextractColors(input, options?): Promise<ExtractColorResult>\nextractColorsFromImageData(imageData, options?): Promise<ExtractColorResult> // browser\nextractColorsFromPixels(input, options?): Promise<ExtractColorResult> // core\n```\n\n`extractColors` is overloaded by the root entrypoint for Browser and Node inputs. `extractColorsFromImageData` is available from `/browser`, and `extractColorsFromPixels` is available from `/core`.\n\n## Supported inputs by runtime\n\n| Runtime | Supported inputs |\n| --- | --- |\n| Browser | `File`, `Blob`, `http://` or `https://` URL string, `HTMLImageElement`, `ImageBitmap`, `HTMLCanvasElement`, `ImageData` |\n| Node.js | `Buffer`, `Uint8Array`, `ArrayBuffer`, URL string (`http://`/`https://`), non-empty local path string |\n| Core | `{ data: Uint8Array \\| Uint8ClampedArray, width: number, height: number, channels: 3 \\| 4 }` |\n\nBrowser URL requests must be allowed by CORS. Browser strings other than `http://` and `https://` URLs are unsupported. Node URL strings are classified as remote when they start with `http://` or `https://`; non-empty other strings are treated as local filesystem paths. An empty Node string is unsupported.\n\n## Neutral result model\n\n`extractColors` returns an `ExtractColorResult`:\n\n```ts\ntype ExtractColorResult = {\n  colors: ObservedColor[]\n  rankings: PaletteRankings\n  metadata: ExtractionMetadata\n}\n```\n\n### Colors\n\n```ts\ntype ObservedColor = {\n  id: ColorId        // e.g. \"color-a85f46\"\n  hex: string         // e.g. \"#a85f46\"\n  rgb: RgbColor       // { r: number, g: number, b: number }\n  lab: LabColor       // { L: number, a: number, b: number }\n  chroma: number      // sqrt(a² + b²)\n  population: number  // count among sampled valid pixels\n  proportion: number  // population / validPixels\n  score: number       // normalized perceptual score (0–1)\n  hsl?: HslColor      // only when result.includeHsl is true\n}\n```\n\nEvery color is an observed color from the sampled valid pixels of the supplied image. No color is generated, adjusted, or assigned a UI role.\n\nThe `score` is relative within a single extraction result — it is not globally comparable across different images or extractions.\n\n`colors` is sorted by ID (lexicographic), not by relevance. Use `rankings` to resolve order.\n\n### Rankings\n\n```ts\ntype PaletteRankings = {\n  perceptual: ColorId[]   // rawScore desc → population desc → chroma desc → id asc\n  population: ColorId[]   // population desc → rawScore desc → chroma desc → id asc\n  chroma: ColorId[]       // chroma desc → rawScore desc → population desc → id asc\n}\n```\n\nEvery ranking contains exactly the same IDs as `colors`. Rankings are permutations of the returned set.\n\nA common consumer pattern to resolve ranked colors:\n\n```ts\nconst colorsById = new Map(\n  result.colors.map(color => [color.id, color]),\n)\nconst perceptual = result.rankings.perceptual.map(\n  id => colorsById.get(id)!,\n)\n```\n\nThis is consumer code — the ranking is resolved directly using `result.rankings`.\n\n### Metadata\n\n```ts\ntype LabKmeansAlgorithmDetails = {\n  algorithm: 'lab-kmeans'\n  requestedClusters: number\n  producedCandidates: number\n  iterations: number\n}\n\ntype MmcqAlgorithmDetails = {\n  algorithm: 'mmcq'\n  requestedBoxes: number\n  producedCandidates: number\n  histogramBits: number\n  occupiedBins: number\n  splits: number\n}\n\ntype AlgorithmDetails =\n  | LabKmeansAlgorithmDetails\n  | MmcqAlgorithmDetails\n\ntype ExtractionMetadata = {\n  algorithm: 'lab-kmeans' | 'mmcq'\n  algorithmVersion: string\n  packageVersion: string\n  runtime: 'browser' | 'node' | 'core'\n  decoder: 'canvas' | 'image-data' | 'sharp' | 'pixels'\n  sampledWidth: number\n  sampledHeight: number\n  sampledPixels: number\n  validPixels: number\n  candidateCount: number\n  returnedColors: number\n  returnedPopulation: number\n  coverage: number       // returnedPopulation / validPixels\n  algorithmDetails: AlgorithmDetails\n}\n```\n\n`metadata.algorithm` and `metadata.algorithmDetails.algorithm` always match. `coverage` may be less than `1` when `maxColors` limits the result. `validPixels` and populations refer to the sampled pixels that passed filtering, not necessarily every source pixel.\n\n### Neutral Color Defaults\n\nThe default options for neutral color extraction (`extractColors`) are exported as `DEFAULT_NEUTRAL_OPTIONS`:\n\n```ts\nimport { DEFAULT_NEUTRAL_OPTIONS } from '@adzazueta/color-extractor'\n```\n\n`DEFAULT_NEUTRAL_OPTIONS` contains the common neutral defaults: `algorithm: 'lab-kmeans'`, `sampling.maxDimension: 150`, filtering values `alphaThreshold: 128`, `minBrightness: 10`, `maxBrightness: 245`, `minSaturation: 8`, result values `maxColors: 5`, `includeHsl: false`, Lab K-means values `clusters: 8`, `iterations: 7`, MMCQ `boxes: 8`, and perceptual ranking values `chromaFloor: 12`, `lowChromaPenalty: 0.1`. Runtime-specific decode options (`decode`) and Node remote options (`remote`) are resolved separately per runtime.\n\n## Configuration\n\nAll options are optional. Defaults favor useful perceptual output and bounded resource use.\n\n### Algorithm selection\n\n| Option | Default | Version | Description |\n| --- | --- | --- | --- |\n| `algorithm` | `'lab-kmeans'` | `1.0.0` | Selects `'lab-kmeans'` or `'mmcq'`. Both algorithms use the same neutral normalization, scoring, rankings, and metadata contract. |\n| `algorithm: 'mmcq'` | - | `mmcq-v2` | Uses a 5-bit-per-channel RGB histogram and selects the nearest observed sample from each final box. |\n\nLab K-means generates Lab-space candidates. MMCQ builds a 5-bit-per-channel RGB histogram, splits color boxes, and selects the nearest observed sample in each final box. The selected version is reported in `metadata.algorithmVersion`.\n\nThe shared neutral ranking uses `chroma * log(population + 1)`. When a candidate's chroma is below `chromaFloor`, the score is multiplied by `lowChromaPenalty`. The normalized `score` is relative to the current extraction result.\n\n### Common options (every runtime)\n\n| Group | Option | Default | Description |\n| --- | --- | --- | --- |\n| `sampling` | `maxDimension` | `150` | Constrain the longest image dimension to this size across browser, Node, and `/core` runtimes. Integer range: 1–4096. In `/core`, downsamples grid sampling to this maximum dimension without modifying the source pixel buffer. |\n| `filtering` | `alphaThreshold` | `128` | Ignore pixels below this alpha value. Integer range: 0–255. |\n| `filtering` | `minBrightness` | `10` | Ignore near-black pixels below this sRGB brightness. Number range: 0–255. |\n| `filtering` | `maxBrightness` | `245` | Ignore near-white pixels above this sRGB brightness. Number range: 0–255. Must be >= `minBrightness`. |\n| `filtering` | `minSaturation` | `8` | Ignore low-saturation pixels below this HSL percentage. Number range: 0–100. |\n| `result` | `maxColors` | `5` | Maximum number of colors in the returned result. Integer range: 1–64. |\n| `result` | `includeHsl` | `false` | Include HSL values in each color. |\n| `advanced.labKmeans` | `clusters` | `max(8, maxColors)` | Internal cluster count. Integer range: 1–64. Must be >= `maxColors`. |\n| `advanced.labKmeans` | `iterations` | `7` | K-means refinement passes. Integer range: 1–100. |\n| `advanced.mmcq` | `boxes` | `max(8, maxColors)` | Requested MMCQ color boxes. Integer range: 1–64. Must be >= `maxColors`. |\n| `advanced.perceptualRanking` | `chromaFloor` | `12` | Chroma below which the low-chroma penalty applies. Number range: 0–150. |\n| `advanced.perceptualRanking` | `lowChromaPenalty` | `0.1` | Score multiplier for low-chroma candidates. Number range: 0–1. |\n| `signal` | — | — | `AbortSignal` for cancellation. |\n\n> **Note:** `result.maxColors` caps the returned color count but is independent of the internal cluster/box count. The active algorithm's count defaults to at least 8 and must be >= `maxColors`.\n\n> **Note:** When `algorithm` is `'lab-kmeans'`, `advanced.mmcq` is rejected. When `algorithm` is `'mmcq'`, `advanced.labKmeans` is rejected. `advanced.perceptualRanking` is valid for both algorithms.\n\n> **Note:** There is no `ranking.strategy` option. All three rankings are always returned.\n\n### Browser decode options\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `decode.maxPixels` | `25_000_000` | Maximum decoded image dimensions (width × height). Integer range: 1–100,000,000. |\n\n### Node decode options\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `decode.maxPixels` | `25_000_000` | Maximum decoded image dimensions (width × height). Integer range: 1–100,000,000. |\n| `decode.animated` | `'first-frame'` | `'first-frame'` extracts only the first frame. |\n| `decode.svg` | `'disabled'` | SVG decoding policy. Enable only for trusted sources. |\n| `decode.respectOrientation` | `true` | Apply EXIF orientation. |\n| `decode.normalizeColorProfile` | `true` | Convert supported images to sRGB. |\n\n### Node remote options\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `remote.timeoutMs` | `10_000` | Request and body timeout in milliseconds. Integer range: 1–300,000. |\n| `remote.maxBytes` | `10_000_000` | Maximum remote response or local file input size in bytes. Integer range: 1–1,000,000,000. |\n| `remote.maxRedirects` | `3` | Maximum redirect hops. Integer range: 0–20. |\n| `remote.allowedProtocols` | `['http:', 'https:']` | Non-empty list of URL protocols allowed for remote input. Values may only be `'http:'` or `'https:'`. |\n| `remote.allowPrivateNetworks` | `false` | Permit private or reserved addresses. Do not enable for untrusted URLs. |\n| `remote.validateContentType` | `true` | Reject non-image response content types when available. |\n\n### Option validation\n\nUnknown, legacy, invalid, `null`, or runtime-incompatible options fail with `COLOR_EXTRACTOR_INVALID_OPTIONS`. Only `undefined` means that an option was omitted and should receive its default. Valid zero values are preserved (e.g. `lowChromaPenalty: 0`).\n\n### Full example\n\n```ts\nimport { extractColors } from '@adzazueta/color-extractor'\n\nconst result = await extractColors(image, {\n  sampling: { maxDimension: 300 },\n  filtering: {\n    alphaThreshold: 16,\n    minSaturation: 5,\n  },\n  result: {\n    maxColors: 8,\n    includeHsl: true,\n  },\n  advanced: {\n    labKmeans: { clusters: 12, iterations: 10 },\n    perceptualRanking: { chromaFloor: 10, lowChromaPenalty: 0.05 },\n  },\n})\n```\n\nFor MMCQ, select the algorithm and configure its boxes instead:\n\n```ts\nconst result = await extractColors(image, {\n  algorithm: 'mmcq',\n  result: { maxColors: 6 },\n  advanced: {\n    mmcq: { boxes: 8 },\n    perceptualRanking: { chromaFloor: 12, lowChromaPenalty: 0.1 },\n  },\n})\n```\n\n## Cancellation\n\nPass an `AbortSignal` to cancel extraction in progress:\n\n```ts\nconst controller = new AbortController()\n\nsetTimeout(() => controller.abort(), 5000)\n\ntry {\n  const result = await extractColors(image, {\n    signal: controller.signal,\n  })\n} catch (error) {\n  if (error instanceof ColorExtractorError && error.code === 'COLOR_EXTRACTOR_ABORTED') {\n    console.log('Cancelled')\n  }\n}\n```\n\nCancellation is checked before work, during supported asynchronous decoding/fetching, after filtering, and after candidate generation. MMCQ also checks during histogram construction, box splitting, and final candidate construction. K-means checks between synchronous refinement iterations; it does not interrupt an iteration that is already running.\n\nAn already-aborted signal rejects immediately without decode, fetch, or sharp work.\n\n## Error handling\n\nLibrary-generated validation, decoding, fetching, and extraction failures use `ColorExtractorError` with a stable `code` property. Platform exceptions that are not recognized by an adapter may propagate unchanged.\n\n```ts\nimport { ColorExtractorError, extractColors } from '@adzazueta/color-extractor'\n\ntry {\n  await extractColors(image)\n} catch (error) {\n  if (error instanceof ColorExtractorError) {\n    console.error(error.code)   // stable machine-readable code\n  }\n}\n```\n\n### Error codes\n\n| Code | Stage | Typical cause |\n| --- | --- | --- |\n| `COLOR_EXTRACTOR_UNSUPPORTED_INPUT` | Pre-processing | Invalid input type, missing fields, or data length mismatch. |\n| `COLOR_EXTRACTOR_INVALID_OPTIONS` | Option resolution | Unknown, legacy, or out-of-range options. |\n| `COLOR_EXTRACTOR_ABORTED` | Any | Operation cancelled via `AbortSignal`. |\n| `COLOR_EXTRACTOR_DECODE_FAILED` | Decode | Image bytes could not be decoded. |\n| `COLOR_EXTRACTOR_CORS_ERROR` | Decode (browser) | Canvas readback blocked by CORS. |\n| `COLOR_EXTRACTOR_FETCH_FAILED` | Fetch | Non-2xx response, network failure, response-body read failure, or empty response. |\n| `COLOR_EXTRACTOR_INPUT_TOO_LARGE` | Input | Browser/Node remote response or Node local file exceeded `maxBytes`. |\n| `COLOR_EXTRACTOR_IMAGE_TOO_LARGE` | Decode | Image dimensions exceeded `maxPixels`. |\n| `COLOR_EXTRACTOR_TIMEOUT` | Fetch | URL request exceeded `timeoutMs`. |\n| `COLOR_EXTRACTOR_UNSAFE_URL` | Fetch (Node) | URL rejected by safety policy. |\n| `COLOR_EXTRACTOR_UNSUPPORTED_FORMAT` | Decode (Node) | Format not supported by the decoder. |\n| `COLOR_EXTRACTOR_SHARP_MISSING` | Decode (Node) | `sharp` is not installed. |\n| `COLOR_EXTRACTOR_NO_VALID_PIXELS` | Filtering | All pixels removed by filtering criteria. |\n\nThe `code` field is the stable machine contract. Error message text may change between releases.\n\n## Browser notes\n\n- Supported inputs: `File`, `Blob`, `http://` or `https://` URL string, `HTMLImageElement`, `ImageBitmap`, `HTMLCanvasElement`, `ImageData`.\n- URL strings are fetched via `fetch()` and subject to CORS.\n- Browser neutral extraction does not expose Node's `remote` options. Browser remote fetches use the built-in 10-second timeout and 10 MB response limit.\n- Decoded images are sampled via `OffscreenCanvas` when available, otherwise a DOM canvas fallback, to `sampling.maxDimension` while preserving aspect ratio. `ImageData` uses a software fallback only when no canvas API is available.\n- Browser `decode.maxPixels` is checked after platform image decoding because browser APIs do not provide portable pre-decode dimensions.\n- The browser bundle contains no Node.js dependencies.\n\n## Node notes\n\n- Supported inputs: `Buffer`, `Uint8Array`, `ArrayBuffer`, URL string, non-empty local path string.\n- Strings starting with `http://` or `https://` are fetched as remote URLs.\n- Non-empty strings that are not HTTP(S) URLs are treated as local filesystem paths. An empty string is unsupported.\n- SVG decoding is disabled by default. Enable with `decode.svg: 'enabled'` for trusted sources.\n- Animated images return the first frame by default (`decode.animated: 'first-frame'`).\n- EXIF orientation is applied by default. Color profiles are normalized to sRGB.\n- SSRF protection: private network requests are blocked by default (`remote.allowPrivateNetworks: false`).\n- `sharp` is an optional peer dependency — install it only when using Node decode paths.\n\n## Core notes\n\n- The core entrypoint accepts already-decoded pixel buffers only.\n- Input requires `{ data, width, height, channels }` with `channels: 3 | 4`.\n- Data length is strictly validated: `data.length === width * height * channels`.\n- The core entrypoint does not resize, decode, or fetch images.\n- `sampling.maxDimension` controls grid downsampling without modifying the source pixel buffer.\n- There is no DOM, filesystem, fetch, or sharp dependency.\n\n## Format support\n\nBrowser support follows the platform decoder. PNG, JPEG, GIF, WebP, and BMP are widely available; AVIF and SVG availability depends on the browser.\n\nNode support follows the installed `sharp` and libvips build. Common formats include PNG, JPEG, WebP, GIF, AVIF, TIFF, BMP, and ICO.\n\n## Known limitations\n\n- Lab K-means (default) and MMCQ (`algorithm: 'mmcq'`) are the available neutral extraction algorithms.\n- No role assignment — every color is an observed color with no semantic label.\n- No generated or adjusted colors (harmony fallback, lightness adjustment).\n- No public ranking helper — use the `Map` pattern shown above.\n- Cancellation granularity is between synchronous pipeline stages and between K-means iterations, not inside one synchronous K-means iteration.\n- Browser and Node decoders may produce slightly different pixel values from the same image.\n\n## color-engine boundary\n\nSemantic role selection (primary, secondary, accent), harmony generation, lightness adjustment, and fallback policies are out of scope for this package.\n\n`extractColors` returns only observed-color evidence. Consumers that need role-labeled colors should compose the extractor with a separate engine layer:\n\n```ts\nconst extracted = await extractColors(image)\nconst theme = colorEngineAdapter(extracted)  // illustrative — adapter name defined by the engine package\n```\n\nThe exact engine adapter API is defined by `@adzazueta/color-engine` and is not part of this package.\n\n## Versioning\n\nThis package follows semantic versioning. The `0.x` major version zero line may introduce breaking changes. The public API surface is documented in this README and in generated TypeScript declarations.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution model, bug reporting, feature requests, and development conventions.\n\n## Security\n\nSee [SECURITY.md](SECURITY.md) for responsible vulnerability-reporting guidance.\n\n## License\n\nMIT © 2026 Alexis D. Zazueta\n","readmeFilename":"README.md"}