chromonym 3.2.0 → 3.3.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/README.md CHANGED
@@ -14,7 +14,8 @@
14
14
  [![Types: included](https://img.shields.io/npm/types/chromonym?color=3178c6&logo=typescript)](https://www.npmjs.com/package/chromonym)
15
15
  [![CI](https://github.com/simiancraft/chromonym/actions/workflows/ci.yml/badge.svg)](https://github.com/simiancraft/chromonym/actions/workflows/ci.yml)
16
16
  [![Coverage](https://img.shields.io/codecov/c/github/simiancraft/chromonym?logo=codecov)](https://codecov.io/github/simiancraft/chromonym)
17
- [![Bundle size](https://img.shields.io/bundlephobia/minzip/chromonym?label=bundle%20size)](https://bundlephobia.com/package/chromonym)
17
+ [![Bundle size](https://img.shields.io/badge/bundle-1.5--20%20kB%20gz-informational)](#what-you-actually-ship)
18
+ [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/simiancraft/chromonym/badge)](https://securityscorecards.dev/viewer/?uri=github.com/simiancraft/chromonym)
18
19
 
19
20
  <p align="center">
20
21
  <code>identify</code> &nbsp;•&nbsp; <code>resolve</code> &nbsp;•&nbsp; <code>convert</code>
@@ -451,17 +452,19 @@ const tryConvert = (input: ColorInput, opts = {}) => {
451
452
 
452
453
  ### What you actually ship
453
454
 
454
- The published tarball is **~557 kB gzipped / ~2.5 MB unpacked** because it carries twelve palettes and ~9000 named-color entries. That's the number `packagephobia` reports — it measures the install footprint on disk. Your **bundle** pays only for what you actually import, measured with `esbuild --minify`:
455
+ **Install size and bundle size are not the same number.** Install size is what lands in your `node_modules/` after `npm install`; bundle size is what ships to your users after tree-shaking, minification, and gzip. For chromonym the gap is roughly two orders of magnitude.
456
+
457
+ The published tarball is **~2.5 MB unpacked / ~557 kB gzipped** because it carries twelve palettes and ~9000 named-color entries; that's the install-size figure `packagephobia` reports. Your **production bundle** pays only for what you actually import, measured with `esbuild --minify`:
455
458
 
456
459
  | Import | min | gzip |
457
460
  |---|---|---|
458
- | `import { identify, web } from 'chromonym'` | 10.9 kB | **4.7 kB** |
459
- | `import { identify, pantone } from 'chromonym'` | 27 kB | **11.3 kB** |
460
- | `import { identify, ntc } from 'chromonym'` (one of the bigger palettes) | 43 kB | **20 kB** |
461
461
  | `import { defineColorPalette } from 'chromonym'` (pure BYO; no built-in) | 3.8 kB | **1.5 kB** |
462
+ | `import { identify, web } from 'chromonym'` | 10.9 kB | **4.7 kB** |
462
463
  | `import { pantoneToRgba } from 'chromonym'` (single conversion) | 17.5 kB | **6.8 kB** |
464
+ | `import { identify, pantone } from 'chromonym'` | 27 kB | **11.3 kB** |
465
+ | `import { identify, ntc } from 'chromonym'` | 43 kB | **20 kB** |
463
466
 
464
- The install-size number is a shipping concern, not a bundle-size one. If you're evaluating chromonym against a size budget, the gzip column above is what actually lands in your production build.
467
+ If you're evaluating chromonym against a size budget, the gzip column above is what actually lands in your production build.
465
468
 
466
469
  ## Types
467
470
 
@@ -3,7 +3,13 @@ import type { Rgba, RgbaInput, RgbInput, RgbString } from '../types.js';
3
3
  * Normalize any RGB/RGBA input shape (string, tuple, or object) into
4
4
  * the canonical Rgba representation. Alpha defaults to 1 when absent.
5
5
  * Rejects non-finite numeric inputs; clamps r/g/b to [0,255] and a to [0,1].
6
- * Throws on malformed strings.
6
+ * Throws on malformed strings. String parsing is linear-time; no polynomial
7
+ * backtracking on pathological input. Leading-dot alpha (e.g. `.5`) is
8
+ * accepted per the CSS Color spec.
9
+ *
10
+ * @example
11
+ * rgbToRgba('rgba(255, 0, 0, .5)');
12
+ * // => { r: 255, g: 0, b: 0, a: 0.5 }
7
13
  */
8
14
  export declare function rgbToRgba(input: RgbInput | RgbaInput): Rgba;
9
15
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"rgb.d.ts","sourceRoot":"","sources":["../../src/conversions/rgb.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAa,SAAS,EAAE,MAAM,aAAa,CAAC;AAanF;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,CA+B3D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAK/C","sourcesContent":["import { clamp, requireFinite } from '../math/clamp.js';\nimport type { Rgba, RgbaInput, RgbInput, RgbObject, RgbString } from '../types.js';\n\nconst RGB_RE =\n /^rgba?\\s*\\(\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*(?:,\\s*(\\d*\\.?\\d+)\\s*)?\\)$/i;\n\nfunction sanitizeChannel(n: unknown, label: string): number {\n return clamp(requireFinite(n, label), 0, 255);\n}\n\nfunction sanitizeAlpha(n: unknown): number {\n return clamp(requireFinite(n, 'alpha'), 0, 1);\n}\n\n/**\n * Normalize any RGB/RGBA input shape (string, tuple, or object) into\n * the canonical Rgba representation. Alpha defaults to 1 when absent.\n * Rejects non-finite numeric inputs; clamps r/g/b to [0,255] and a to [0,1].\n * Throws on malformed strings.\n */\nexport function rgbToRgba(input: RgbInput | RgbaInput): Rgba {\n if (Array.isArray(input)) {\n const rawA = input.length === 4 ? input[3] : 1;\n return {\n r: sanitizeChannel(input[0], 'r'),\n g: sanitizeChannel(input[1], 'g'),\n b: sanitizeChannel(input[2], 'b'),\n a: sanitizeAlpha(rawA),\n };\n }\n\n if (typeof input === 'string') {\n const match = RGB_RE.exec(input);\n if (!match) throw new Error(`Invalid rgb(a) string: ${input}`);\n return {\n r: sanitizeChannel(Number(match[1]), 'r'),\n g: sanitizeChannel(Number(match[2]), 'g'),\n b: sanitizeChannel(Number(match[3]), 'b'),\n a: sanitizeAlpha(match[4] !== undefined ? Number(match[4]) : 1),\n };\n }\n\n // Object form — TypeScript's Array.isArray narrowing doesn't exclude\n // readonly tuple types from the union, so cast after the guards.\n const obj = input as RgbObject | Rgba;\n return {\n r: sanitizeChannel(obj.r, 'r'),\n g: sanitizeChannel(obj.g, 'g'),\n b: sanitizeChannel(obj.b, 'b'),\n a: sanitizeAlpha('a' in obj ? obj.a : 1),\n };\n}\n\n/**\n * Emit an `rgb(r, g, b)` string. Alpha is dropped; values are\n * already-clamped by rgbToRgba, but we re-clamp defensively for direct\n * Rgba-object callers who bypass the input normalizer.\n */\nexport function rgbaToRgb(rgba: Rgba): RgbString {\n const r = clamp(Math.round(rgba.r), 0, 255);\n const g = clamp(Math.round(rgba.g), 0, 255);\n const b = clamp(Math.round(rgba.b), 0, 255);\n return `rgb(${r}, ${g}, ${b})` as RgbString;\n}\n"]}
1
+ {"version":3,"file":"rgb.d.ts","sourceRoot":"","sources":["../../src/conversions/rgb.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAa,SAAS,EAAE,MAAM,aAAa,CAAC;AAgBnF;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,CA+B3D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAK/C","sourcesContent":["import { clamp, requireFinite } from '../math/clamp.js';\nimport type { Rgba, RgbaInput, RgbInput, RgbObject, RgbString } from '../types.js';\n\n// Alpha group split into two unambiguous alternatives to avoid polynomial\n// backtracking (CodeQL js/polynomial-redos) on malformed inputs like\n// `rgb(9,9,9,99...9`.\nconst RGB_RE =\n /^rgba?\\s*\\(\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*(?:,\\s*(\\d+(?:\\.\\d+)?|\\.\\d+)\\s*)?\\)$/i;\n\nfunction sanitizeChannel(n: unknown, label: string): number {\n return clamp(requireFinite(n, label), 0, 255);\n}\n\nfunction sanitizeAlpha(n: unknown): number {\n return clamp(requireFinite(n, 'alpha'), 0, 1);\n}\n\n/**\n * Normalize any RGB/RGBA input shape (string, tuple, or object) into\n * the canonical Rgba representation. Alpha defaults to 1 when absent.\n * Rejects non-finite numeric inputs; clamps r/g/b to [0,255] and a to [0,1].\n * Throws on malformed strings. String parsing is linear-time; no polynomial\n * backtracking on pathological input. Leading-dot alpha (e.g. `.5`) is\n * accepted per the CSS Color spec.\n *\n * @example\n * rgbToRgba('rgba(255, 0, 0, .5)');\n * // => { r: 255, g: 0, b: 0, a: 0.5 }\n */\nexport function rgbToRgba(input: RgbInput | RgbaInput): Rgba {\n if (Array.isArray(input)) {\n const rawA = input.length === 4 ? input[3] : 1;\n return {\n r: sanitizeChannel(input[0], 'r'),\n g: sanitizeChannel(input[1], 'g'),\n b: sanitizeChannel(input[2], 'b'),\n a: sanitizeAlpha(rawA),\n };\n }\n\n if (typeof input === 'string') {\n const match = RGB_RE.exec(input);\n if (!match) throw new Error(`Invalid rgb(a) string: ${input}`);\n return {\n r: sanitizeChannel(Number(match[1]), 'r'),\n g: sanitizeChannel(Number(match[2]), 'g'),\n b: sanitizeChannel(Number(match[3]), 'b'),\n a: sanitizeAlpha(match[4] !== undefined ? Number(match[4]) : 1),\n };\n }\n\n // Object form — TypeScript's Array.isArray narrowing doesn't exclude\n // readonly tuple types from the union, so cast after the guards.\n const obj = input as RgbObject | Rgba;\n return {\n r: sanitizeChannel(obj.r, 'r'),\n g: sanitizeChannel(obj.g, 'g'),\n b: sanitizeChannel(obj.b, 'b'),\n a: sanitizeAlpha('a' in obj ? obj.a : 1),\n };\n}\n\n/**\n * Emit an `rgb(r, g, b)` string. Alpha is dropped; values are\n * already-clamped by rgbToRgba, but we re-clamp defensively for direct\n * Rgba-object callers who bypass the input normalizer.\n */\nexport function rgbaToRgb(rgba: Rgba): RgbString {\n const r = clamp(Math.round(rgba.r), 0, 255);\n const g = clamp(Math.round(rgba.g), 0, 255);\n const b = clamp(Math.round(rgba.b), 0, 255);\n return `rgb(${r}, ${g}, ${b})` as RgbString;\n}\n"]}
@@ -1,5 +1,8 @@
1
1
  import { clamp, requireFinite } from '../math/clamp.js';
2
- const RGB_RE = /^rgba?\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*(?:,\s*(\d*\.?\d+)\s*)?\)$/i;
2
+ // Alpha group split into two unambiguous alternatives to avoid polynomial
3
+ // backtracking (CodeQL js/polynomial-redos) on malformed inputs like
4
+ // `rgb(9,9,9,99...9`.
5
+ const RGB_RE = /^rgba?\s*\(\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*(?:,\s*(\d+(?:\.\d+)?|\.\d+)\s*)?\)$/i;
3
6
  function sanitizeChannel(n, label) {
4
7
  return clamp(requireFinite(n, label), 0, 255);
5
8
  }
@@ -10,7 +13,13 @@ function sanitizeAlpha(n) {
10
13
  * Normalize any RGB/RGBA input shape (string, tuple, or object) into
11
14
  * the canonical Rgba representation. Alpha defaults to 1 when absent.
12
15
  * Rejects non-finite numeric inputs; clamps r/g/b to [0,255] and a to [0,1].
13
- * Throws on malformed strings.
16
+ * Throws on malformed strings. String parsing is linear-time; no polynomial
17
+ * backtracking on pathological input. Leading-dot alpha (e.g. `.5`) is
18
+ * accepted per the CSS Color spec.
19
+ *
20
+ * @example
21
+ * rgbToRgba('rgba(255, 0, 0, .5)');
22
+ * // => { r: 255, g: 0, b: 0, a: 0.5 }
14
23
  */
15
24
  export function rgbToRgba(input) {
16
25
  if (Array.isArray(input)) {
@@ -1 +1 @@
1
- {"version":3,"file":"rgb.js","sourceRoot":"","sources":["../../src/conversions/rgb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGxD,MAAM,MAAM,GACV,+GAA+G,CAAC;AAElH,SAAS,eAAe,CAAC,CAAU,EAAE,KAAa;IAChD,OAAO,KAAK,CAAC,aAAa,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,aAAa,CAAC,CAAU;IAC/B,OAAO,KAAK,CAAC,aAAa,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;AAChD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,KAA2B;IACnD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/C,OAAO;YACL,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;YACjC,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;YACjC,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;YACjC,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC;SACvB,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;QAC/D,OAAO;YACL,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;YACzC,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;YACzC,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;YACzC,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAChE,CAAC;IACJ,CAAC;IAED,qEAAqE;IACrE,iEAAiE;IACjE,MAAM,GAAG,GAAG,KAAyB,CAAC;IACtC,OAAO;QACL,CAAC,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC;QAC9B,CAAC,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC;QAC9B,CAAC,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC;QAC9B,CAAC,EAAE,aAAa,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACzC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,IAAU;IAClC,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5C,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5C,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5C,OAAO,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAgB,CAAC;AAC9C,CAAC","sourcesContent":["import { clamp, requireFinite } from '../math/clamp.js';\nimport type { Rgba, RgbaInput, RgbInput, RgbObject, RgbString } from '../types.js';\n\nconst RGB_RE =\n /^rgba?\\s*\\(\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*(?:,\\s*(\\d*\\.?\\d+)\\s*)?\\)$/i;\n\nfunction sanitizeChannel(n: unknown, label: string): number {\n return clamp(requireFinite(n, label), 0, 255);\n}\n\nfunction sanitizeAlpha(n: unknown): number {\n return clamp(requireFinite(n, 'alpha'), 0, 1);\n}\n\n/**\n * Normalize any RGB/RGBA input shape (string, tuple, or object) into\n * the canonical Rgba representation. Alpha defaults to 1 when absent.\n * Rejects non-finite numeric inputs; clamps r/g/b to [0,255] and a to [0,1].\n * Throws on malformed strings.\n */\nexport function rgbToRgba(input: RgbInput | RgbaInput): Rgba {\n if (Array.isArray(input)) {\n const rawA = input.length === 4 ? input[3] : 1;\n return {\n r: sanitizeChannel(input[0], 'r'),\n g: sanitizeChannel(input[1], 'g'),\n b: sanitizeChannel(input[2], 'b'),\n a: sanitizeAlpha(rawA),\n };\n }\n\n if (typeof input === 'string') {\n const match = RGB_RE.exec(input);\n if (!match) throw new Error(`Invalid rgb(a) string: ${input}`);\n return {\n r: sanitizeChannel(Number(match[1]), 'r'),\n g: sanitizeChannel(Number(match[2]), 'g'),\n b: sanitizeChannel(Number(match[3]), 'b'),\n a: sanitizeAlpha(match[4] !== undefined ? Number(match[4]) : 1),\n };\n }\n\n // Object form — TypeScript's Array.isArray narrowing doesn't exclude\n // readonly tuple types from the union, so cast after the guards.\n const obj = input as RgbObject | Rgba;\n return {\n r: sanitizeChannel(obj.r, 'r'),\n g: sanitizeChannel(obj.g, 'g'),\n b: sanitizeChannel(obj.b, 'b'),\n a: sanitizeAlpha('a' in obj ? obj.a : 1),\n };\n}\n\n/**\n * Emit an `rgb(r, g, b)` string. Alpha is dropped; values are\n * already-clamped by rgbToRgba, but we re-clamp defensively for direct\n * Rgba-object callers who bypass the input normalizer.\n */\nexport function rgbaToRgb(rgba: Rgba): RgbString {\n const r = clamp(Math.round(rgba.r), 0, 255);\n const g = clamp(Math.round(rgba.g), 0, 255);\n const b = clamp(Math.round(rgba.b), 0, 255);\n return `rgb(${r}, ${g}, ${b})` as RgbString;\n}\n"]}
1
+ {"version":3,"file":"rgb.js","sourceRoot":"","sources":["../../src/conversions/rgb.ts"],"names":[],"mappings":"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","sourcesContent":["import { clamp, requireFinite } from '../math/clamp.js';\nimport type { Rgba, RgbaInput, RgbInput, RgbObject, RgbString } from '../types.js';\n\n// Alpha group split into two unambiguous alternatives to avoid polynomial\n// backtracking (CodeQL js/polynomial-redos) on malformed inputs like\n// `rgb(9,9,9,99...9`.\nconst RGB_RE =\n /^rgba?\\s*\\(\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*,\\s*(-?\\d+(?:\\.\\d+)?)\\s*(?:,\\s*(\\d+(?:\\.\\d+)?|\\.\\d+)\\s*)?\\)$/i;\n\nfunction sanitizeChannel(n: unknown, label: string): number {\n return clamp(requireFinite(n, label), 0, 255);\n}\n\nfunction sanitizeAlpha(n: unknown): number {\n return clamp(requireFinite(n, 'alpha'), 0, 1);\n}\n\n/**\n * Normalize any RGB/RGBA input shape (string, tuple, or object) into\n * the canonical Rgba representation. Alpha defaults to 1 when absent.\n * Rejects non-finite numeric inputs; clamps r/g/b to [0,255] and a to [0,1].\n * Throws on malformed strings. String parsing is linear-time; no polynomial\n * backtracking on pathological input. Leading-dot alpha (e.g. `.5`) is\n * accepted per the CSS Color spec.\n *\n * @example\n * rgbToRgba('rgba(255, 0, 0, .5)');\n * // => { r: 255, g: 0, b: 0, a: 0.5 }\n */\nexport function rgbToRgba(input: RgbInput | RgbaInput): Rgba {\n if (Array.isArray(input)) {\n const rawA = input.length === 4 ? input[3] : 1;\n return {\n r: sanitizeChannel(input[0], 'r'),\n g: sanitizeChannel(input[1], 'g'),\n b: sanitizeChannel(input[2], 'b'),\n a: sanitizeAlpha(rawA),\n };\n }\n\n if (typeof input === 'string') {\n const match = RGB_RE.exec(input);\n if (!match) throw new Error(`Invalid rgb(a) string: ${input}`);\n return {\n r: sanitizeChannel(Number(match[1]), 'r'),\n g: sanitizeChannel(Number(match[2]), 'g'),\n b: sanitizeChannel(Number(match[3]), 'b'),\n a: sanitizeAlpha(match[4] !== undefined ? Number(match[4]) : 1),\n };\n }\n\n // Object form — TypeScript's Array.isArray narrowing doesn't exclude\n // readonly tuple types from the union, so cast after the guards.\n const obj = input as RgbObject | Rgba;\n return {\n r: sanitizeChannel(obj.r, 'r'),\n g: sanitizeChannel(obj.g, 'g'),\n b: sanitizeChannel(obj.b, 'b'),\n a: sanitizeAlpha('a' in obj ? obj.a : 1),\n };\n}\n\n/**\n * Emit an `rgb(r, g, b)` string. Alpha is dropped; values are\n * already-clamped by rgbToRgba, but we re-clamp defensively for direct\n * Rgba-object callers who bypass the input normalizer.\n */\nexport function rgbaToRgb(rgba: Rgba): RgbString {\n const r = clamp(Math.round(rgba.r), 0, 255);\n const g = clamp(Math.round(rgba.g), 0, 255);\n const b = clamp(Math.round(rgba.b), 0, 255);\n return `rgb(${r}, ${g}, ${b})` as RgbString;\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "chromonym",
3
- "version": "3.2.0",
4
- "description": "One color-naming API for any palette. Identify, resolve, and convert colors across your own palette or the built-in CSS, X11, Pantone, and Crayola sets — fully typed, tree-shakeable, perceptual distance metrics (CIEDE2000 / OKLAB).",
3
+ "version": "3.3.1",
4
+ "description": "One color-naming API for any palette. Identify, resolve, and convert colors across your own palette or twelve built-in sets (CSS, X11, Pantone, Crayola, NTC, XKCD, Resene, NCS, ISCC-NBS, NBS, FS 595 B/C); fully typed, tree-shakeable, perceptual distance metrics (CIEDE2000 / OKLAB).",
5
5
  "keywords": [
6
6
  "color",
7
7
  "color-name",