@mapbox/mapbox-gl-style-spec
Version:
a specification for mapbox gl styles
1 lines • 959 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../../../node_modules/json-stringify-pretty-compact/index.js","../format.ts","../visit.ts","../migrate/v8.ts","../util/extend.ts","../expression/parsing_error.ts","../expression/scope.ts","../expression/types.ts","../../../node_modules/csscolorparser/csscolorparser.js","../util/interpolate.ts","../util/color.ts","../expression/types/collator.ts","../expression/types/formatted.ts","../expression/types/image_id.ts","../expression/types/image_variant.ts","../expression/types/resolved_image.ts","../expression/values.ts","../expression/definitions/literal.ts","../expression/runtime_error.ts","../expression/definitions/assertion.ts","../expression/definitions/format.ts","../expression/definitions/image.ts","../util/get_type.ts","../expression/definitions/coercion.ts","../expression/evaluation_context.ts","../expression/compound_expression.ts","../expression/definitions/collator.ts","../../../node_modules/@mapbox/point-geometry/index.js","../util/geometry_util.ts","../expression/definitions/within.ts","../../../node_modules/cheap-ruler/index.js","../../../node_modules/tinyqueue/index.js","../data/extent.ts","../expression/definitions/distance.ts","../expression/is_constant.ts","../expression/definitions/config.ts","../expression/definitions/var.ts","../expression/parsing_context.ts","../expression/stops.ts","../expression/definitions/step.ts","../../../node_modules/@mapbox/unitbezier/index.js","../util/color_spaces.ts","../expression/definitions/interpolate.ts","../expression/definitions/coalesce.ts","../expression/definitions/let.ts","../expression/definitions/at.ts","../expression/definitions/at_interpolated.ts","../expression/definitions/in.ts","../expression/definitions/index_of.ts","../expression/definitions/match.ts","../expression/definitions/case.ts","../expression/definitions/slice.ts","../expression/definitions/comparison.ts","../expression/definitions/number_format.ts","../expression/definitions/length.ts","../util/random.ts","../expression/definitions/index.ts","../util/result.ts","../util/properties.ts","../function/index.ts","../expression/index.ts","../function/convert.ts","../util/unbundle_jsonlint.ts","../feature_filter/index.ts","../feature_filter/convert.ts","../migrate/expressions.ts","../migrate.ts","../composite.ts","../util/ref_properties.ts","../deref.ts","../util/deep_equal.ts","../diff.ts","../error/validation_error.ts","../error/parsing_error.ts","../validate/validate_object.ts","../validate/validate_import.ts","../validate/validate_array.ts","../validate/validate_number.ts","../validate/validate_function.ts","../validate/validate_expression.ts","../validate/validate_boolean.ts","../validate/validate_color.ts","../validate/validate_enum.ts","../validate/validate_filter.ts","../validate/validate_property.ts","../validate/validate_paint_property.ts","../validate/validate_layout_property.ts","../validate/validate_layer.ts","../validate/validate_string.ts","../validate/validate_source.ts","../validate/validate_model.ts","../validate/validate_light.ts","../validate/validate_lights.ts","../validate/validate_terrain.ts","../validate/validate_fog.ts","../validate/validate_formatted.ts","../validate/validate_image.ts","../validate/validate_projection.ts","../validate/validate_iconset.ts","../validate/validate.ts","../validate/validate_glyphs_url.ts","../validate/validate_style.ts","../validate_style.min.ts","../../../node_modules/@mapbox/jsonlint-lines-primitives/lib/jsonlint.js","../read_style.ts","../validate_style.ts","../validate_mapbox_api_supported.ts","../style-spec.ts"],"sourcesContent":["// Note: This regex matches even invalid JSON strings, but since we’re\n// working on the output of `JSON.stringify` we know that only valid strings\n// are present (unless the user supplied a weird `options.indent` but in\n// that case we don’t care since the output would be invalid anyway).\nconst stringOrChar = /(\"(?:[^\\\\\"]|\\\\.)*\")|[:,]/g;\n\nexport default function stringify(passedObj, options = {}) {\n const indent = JSON.stringify(\n [1],\n undefined,\n options.indent === undefined ? 2 : options.indent\n ).slice(2, -3);\n\n const maxLength =\n indent === \"\"\n ? Infinity\n : options.maxLength === undefined\n ? 80\n : options.maxLength;\n\n let { replacer } = options;\n\n return (function _stringify(obj, currentIndent, reserved) {\n if (obj && typeof obj.toJSON === \"function\") {\n obj = obj.toJSON();\n }\n\n const string = JSON.stringify(obj, replacer);\n\n if (string === undefined) {\n return string;\n }\n\n const length = maxLength - currentIndent.length - reserved;\n\n if (string.length <= length) {\n const prettified = string.replace(\n stringOrChar,\n (match, stringLiteral) => {\n return stringLiteral || `${match} `;\n }\n );\n if (prettified.length <= length) {\n return prettified;\n }\n }\n\n if (replacer != null) {\n obj = JSON.parse(string);\n replacer = undefined;\n }\n\n if (typeof obj === \"object\" && obj !== null) {\n const nextIndent = currentIndent + indent;\n const items = [];\n let index = 0;\n let start;\n let end;\n\n if (Array.isArray(obj)) {\n start = \"[\";\n end = \"]\";\n const { length } = obj;\n for (; index < length; index++) {\n items.push(\n _stringify(obj[index], nextIndent, index === length - 1 ? 0 : 1) ||\n \"null\"\n );\n }\n } else {\n start = \"{\";\n end = \"}\";\n const keys = Object.keys(obj);\n const { length } = keys;\n for (; index < length; index++) {\n const key = keys[index];\n const keyPart = `${JSON.stringify(key)}: `;\n const value = _stringify(\n obj[key],\n nextIndent,\n keyPart.length + (index === length - 1 ? 0 : 1)\n );\n if (value !== undefined) {\n items.push(keyPart + value);\n }\n }\n }\n\n if (items.length > 0) {\n return [start, indent + items.join(`,\\n${nextIndent}`), end].join(\n `\\n${currentIndent}`\n );\n }\n }\n\n return string;\n })(passedObj, \"\", 0);\n}\n","/* eslint-disable @typescript-eslint/ban-ts-comment */\n// @ts-nocheck\n\nimport reference from './reference/latest';\nimport stringifyPretty from 'json-stringify-pretty-compact';\n\nfunction sortKeysBy(obj, reference) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const result: Record<string, any> = {};\n for (const key in reference) {\n if (obj[key] !== undefined) {\n result[key] = obj[key];\n }\n }\n for (const key in obj) {\n if (result[key] === undefined) {\n result[key] = obj[key];\n }\n }\n return result;\n}\n\n/**\n * Format a Mapbox GL Style. Returns a stringified style with its keys\n * sorted in the same order as the reference style.\n *\n * The optional `space` argument is passed to\n * [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)\n * to generate formatted output.\n *\n * If `space` is unspecified, a default of `2` spaces will be used.\n *\n * @private\n * @param {Object} style a Mapbox GL Style\n * @param {number} [space] space argument to pass to `JSON.stringify`\n * @returns {string} stringified formatted JSON\n * @example\n * var fs = require('fs');\n * var format = require('mapbox-gl-style-spec').format;\n * var style = fs.readFileSync('./source.json', 'utf8');\n * fs.writeFileSync('./dest.json', format(style));\n * fs.writeFileSync('./dest.min.json', format(style, 0));\n */\nfunction format(style, space = 2) {\n style = sortKeysBy(style, reference.$root);\n\n if (style.layers) {\n style.layers = style.layers.map((layer) => sortKeysBy(layer, reference.layer));\n }\n\n return stringifyPretty(style, {indent: space});\n}\n\nexport default format;\n","import Reference from './reference/v8.json';\n\nimport type {StylePropertySpecification} from './style-spec';\nimport type {\n StyleSpecification,\n SourceSpecification,\n LayerSpecification,\n PropertyValueSpecification\n} from './types';\n\nfunction getPropertyReference(propertyName: string): StylePropertySpecification {\n for (let i = 0; i < Reference.layout.length; i++) {\n for (const key in Reference[Reference.layout[i]]) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n if (key === propertyName) return Reference[Reference.layout[i]][key];\n }\n }\n for (let i = 0; i < Reference.paint.length; i++) {\n for (const key in Reference[Reference.paint[i]]) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n if (key === propertyName) return Reference[Reference.paint[i]][key];\n }\n }\n\n return null;\n}\n\nexport function eachSource(style: StyleSpecification, callback: (_: SourceSpecification) => void) {\n for (const k in style.sources) {\n callback(style.sources[k]);\n }\n}\n\nexport function eachLayer(style: StyleSpecification, callback: (_: LayerSpecification) => void) {\n for (const layer of style.layers) {\n callback(layer);\n }\n}\n\ntype PropertyCallback = (\n arg1: {\n path: [string, 'paint' | 'layout', string] // [layerid, paint/layout, property key];\n key: string;\n value: PropertyValueSpecification<unknown> ;\n reference: StylePropertySpecification;\n set: (\n arg1: PropertyValueSpecification<unknown>,\n ) => void;\n },\n) => void;\n\nexport function eachProperty(\n style: StyleSpecification,\n options: {\n paint?: boolean;\n layout?: boolean;\n },\n callback: PropertyCallback\n) {\n function inner(layer: LayerSpecification, propertyType: 'paint' | 'layout') {\n if (layer.type === 'slot' || layer.type === 'clip') return;\n const properties = layer[propertyType];\n if (!properties) return;\n Object.keys(properties).forEach((key) => {\n callback({\n path: [layer.id, propertyType, key],\n key,\n value: properties[key],\n reference: getPropertyReference(key),\n set(x) {\n properties[key] = x;\n }\n });\n });\n }\n\n eachLayer(style, (layer) => {\n if (options.paint) {\n inner(layer, 'paint');\n }\n if (options.layout) {\n inner(layer, 'layout');\n }\n });\n}\n","/* eslint-disable @typescript-eslint/ban-ts-comment */\n// @ts-nocheck\nimport {eachSource, eachLayer, eachProperty} from '../visit';\n\nfunction eachLayout(layer, callback) {\n for (const k in layer) {\n if (k.indexOf('layout') === 0) {\n callback(layer[k], k);\n }\n }\n}\n\nfunction eachPaint(layer, callback) {\n for (const k in layer) {\n if (k.indexOf('paint') === 0) {\n callback(layer[k], k);\n }\n }\n}\n\nfunction resolveConstant(style, value) {\n if (typeof value === 'string' && value[0] === '@') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return resolveConstant(style, style.constants[value]);\n } else {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value;\n }\n}\n\nfunction isFunction(value) {\n return Array.isArray(value.stops);\n}\n\nfunction renameProperty(obj, from, to) {\n obj[to] = obj[from]; delete obj[from];\n}\n\nexport default function (style) {\n style.version = 8;\n\n // Rename properties, reverse coordinates in source and layers\n eachSource(style, (source) => {\n if (source.type === 'video' && source.url !== undefined) {\n renameProperty(source, 'url', 'urls');\n }\n if (source.type === 'video') {\n source.coordinates.forEach((coord) => {\n return coord.reverse();\n });\n }\n });\n\n eachLayer(style, (layer) => {\n eachLayout(layer, (layout) => {\n if (layout['symbol-min-distance'] !== undefined) {\n renameProperty(layout, 'symbol-min-distance', 'symbol-spacing');\n }\n });\n\n eachPaint(layer, (paint) => {\n if (paint['background-image'] !== undefined) {\n renameProperty(paint, 'background-image', 'background-pattern');\n }\n if (paint['line-image'] !== undefined) {\n renameProperty(paint, 'line-image', 'line-pattern');\n }\n if (paint['fill-image'] !== undefined) {\n renameProperty(paint, 'fill-image', 'fill-pattern');\n }\n });\n });\n\n // Inline Constants\n eachProperty(style, {paint: true, layout: true}, (property) => {\n const value = resolveConstant(style, property.value);\n\n if (isFunction(value)) {\n value.stops.forEach((stop) => {\n stop[1] = resolveConstant(style, stop[1]);\n });\n }\n\n property.set(value);\n });\n delete style.constants;\n\n eachLayer(style, (layer) => {\n // get rid of text-max-size, icon-max-size\n // turn text-size, icon-size into layout properties\n // https://github.com/mapbox/mapbox-gl-style-spec/issues/255\n\n eachLayout(layer, (layout) => {\n delete layout['text-max-size'];\n delete layout['icon-max-size'];\n });\n\n eachPaint(layer, (paint) => {\n if (paint['text-size']) {\n if (!layer.layout) layer.layout = {};\n layer.layout['text-size'] = paint['text-size'];\n delete paint['text-size'];\n }\n\n if (paint['icon-size']) {\n if (!layer.layout) layer.layout = {};\n layer.layout['icon-size'] = paint['icon-size'];\n delete paint['icon-size'];\n }\n });\n });\n\n function migrateFontstackURL(input) {\n const inputParsed = new URL(input);\n const inputPathnameParts = inputParsed.pathname.split('/');\n\n if (inputParsed.protocol !== 'mapbox:') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return input;\n\n } else if (inputParsed.hostname === 'fontstack') {\n assert(decodeURI(inputParsed.pathname) === '/{fontstack}/{range}.pbf');\n return 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf';\n\n } else if (inputParsed.hostname === 'fonts') {\n assert(inputPathnameParts[1] === 'v1');\n assert(decodeURI(inputPathnameParts[3]) === '{fontstack}');\n assert(decodeURI(inputPathnameParts[4]) === '{range}.pbf');\n return `mapbox://fonts/${inputPathnameParts[2]}/{fontstack}/{range}.pbf`;\n\n } else {\n assert(false);\n }\n\n function assert(predicate) {\n if (!predicate) {\n throw new Error(`Invalid font url: \"${input}\"`);\n }\n }\n }\n\n if (style.glyphs) {\n style.glyphs = migrateFontstackURL(style.glyphs);\n }\n\n function migrateFontStack(font) {\n function splitAndTrim(string) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return string.split(',').map((s) => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return s.trim();\n });\n }\n\n if (Array.isArray(font)) {\n // Assume it's a previously migrated font-array.\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return font;\n\n } else if (typeof font === 'string') {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return splitAndTrim(font);\n\n } else if (typeof font === 'object') {\n font.stops.forEach((stop) => {\n stop[1] = splitAndTrim(stop[1]);\n });\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return font;\n\n } else {\n throw new Error(\"unexpected font value\");\n }\n }\n\n eachLayer(style, (layer) => {\n eachLayout(layer, (layout) => {\n if (layout['text-font']) {\n layout['text-font'] = migrateFontStack(layout['text-font']);\n }\n });\n });\n\n // Reverse order of symbol layers. This is an imperfect migration.\n //\n // The order of a symbol layer in the layers list affects two things:\n // - how it is drawn relative to other layers (like oneway arrows below bridges)\n // - the placement priority compared to other layers\n //\n // It's impossible to reverse the placement priority without breaking the draw order\n // in some cases. This migration only reverses the order of symbol layers that\n // are above all other types of layers.\n //\n // Symbol layers that are at the top of the map preserve their priority.\n // Symbol layers that are below another type (line, fill) of layer preserve their draw order.\n\n let firstSymbolLayer = 0;\n for (let i = style.layers.length - 1; i >= 0; i--) {\n const layer = style.layers[i];\n if (layer.type !== 'symbol') {\n firstSymbolLayer = i + 1;\n break;\n }\n }\n\n const symbolLayers = style.layers.splice(firstSymbolLayer);\n symbolLayers.reverse();\n style.layers = style.layers.concat(symbolLayers);\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return style;\n}\n","// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport default function (output: any, ...inputs: Array<any>): any {\n for (const input of inputs) {\n for (const k in input) {\n output[k] = input[k];\n }\n }\n return output;\n}\n","class ParsingError extends Error {\n key: string;\n override message: string;\n constructor(key: string, message: string) {\n super(message);\n this.message = message;\n this.key = key;\n }\n}\n\nexport default ParsingError;\n","import type {Expression} from './expression';\n\n/**\n * Tracks `let` bindings during expression parsing.\n * @private\n */\nclass Scope {\n parent: Scope | null | undefined;\n bindings: {\n [_: string]: Expression;\n };\n constructor(parent?: Scope, bindings: Array<[string, Expression]> = []) {\n this.parent = parent;\n this.bindings = {};\n for (const [name, expression] of bindings) {\n this.bindings[name] = expression;\n }\n }\n\n concat(bindings: Array<[string, Expression]>): Scope {\n return new Scope(this, bindings);\n }\n\n get(name: string): Expression {\n if (this.bindings[name]) { return this.bindings[name]; }\n if (this.parent) { return this.parent.get(name); }\n throw new Error(`${name} not found in scope.`);\n }\n\n has(name: string): boolean {\n if (this.bindings[name]) return true;\n return this.parent ? this.parent.has(name) : false;\n }\n}\n\nexport default Scope;\n","export type NullTypeT = {\n kind: 'null';\n};\nexport type NumberTypeT = {\n kind: 'number';\n};\nexport type StringTypeT = {\n kind: 'string';\n};\nexport type BooleanTypeT = {\n kind: 'boolean';\n};\nexport type ColorTypeT = {\n kind: 'color';\n};\nexport type ObjectTypeT = {\n kind: 'object';\n};\nexport type ValueTypeT = {\n kind: 'value';\n};\nexport type ErrorTypeT = {\n kind: 'error';\n};\nexport type CollatorTypeT = {\n kind: 'collator';\n};\nexport type FormattedTypeT = {\n kind: 'formatted';\n};\nexport type ResolvedImageTypeT = {\n kind: 'resolvedImage';\n};\n\nexport type EvaluationKind = 'constant' | 'source' | 'camera' | 'composite';\n\nexport type Type = NullTypeT | NumberTypeT | StringTypeT | BooleanTypeT | ColorTypeT | ObjectTypeT | ValueTypeT |\nArrayType | ErrorTypeT | CollatorTypeT | FormattedTypeT | ResolvedImageTypeT;\n\nexport type ArrayType = {\n kind: 'array';\n itemType: Type;\n N: number | null | undefined;\n};\n\nexport type NativeType = 'number' | 'string' | 'boolean' | 'null' | 'array' | 'object';\n\nexport const NullType = {kind: 'null'} as const;\nexport const NumberType = {kind: 'number'} as const;\nexport const StringType = {kind: 'string'} as const;\nexport const BooleanType = {kind: 'boolean'} as const;\nexport const ColorType = {kind: 'color'} as const;\nexport const ObjectType = {kind: 'object'} as const;\nexport const ValueType = {kind: 'value'} as const;\nexport const ErrorType = {kind: 'error'} as const;\nexport const CollatorType = {kind: 'collator'} as const;\nexport const FormattedType = {kind: 'formatted'} as const;\nexport const ResolvedImageType = {kind: 'resolvedImage'} as const;\n\nexport function array(itemType: Type, N?: number | null): ArrayType {\n return {\n kind: 'array',\n itemType,\n N\n };\n}\n\nexport function toString(type: Type): string {\n if (type.kind === 'array') {\n const itemType = toString(type.itemType);\n return typeof type.N === 'number' ?\n `array<${itemType}, ${type.N}>` :\n type.itemType.kind === 'value' ? 'array' : `array<${itemType}>`;\n } else {\n return type.kind;\n }\n}\n\nconst valueMemberTypes = [\n NullType,\n NumberType,\n StringType,\n BooleanType,\n ColorType,\n FormattedType,\n ObjectType,\n array(ValueType),\n ResolvedImageType\n];\n\n/**\n * Returns null if `t` is a subtype of `expected`; otherwise returns an\n * error message.\n * @private\n */\nexport function checkSubtype(expected: Type, t: Type): string | null | undefined {\n if (t.kind === 'error') {\n // Error is a subtype of every type\n return null;\n } else if (expected.kind === 'array') {\n if (t.kind === 'array' &&\n ((t.N === 0 && t.itemType.kind === 'value') || !checkSubtype(expected.itemType, t.itemType)) &&\n (typeof expected.N !== 'number' || expected.N === t.N)) {\n return null;\n }\n } else if (expected.kind === t.kind) {\n return null;\n } else if (expected.kind === 'value') {\n for (const memberType of valueMemberTypes) {\n if (!checkSubtype(memberType, t)) {\n return null;\n }\n }\n }\n\n return `Expected ${toString(expected)} but found ${toString(t)} instead.`;\n}\n\nexport function isValidType(provided: Type, allowedTypes: Array<Type>): boolean {\n return allowedTypes.some(t => t.kind === provided.kind);\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function isValidNativeType(provided: any, allowedTypes: Array<NativeType>): boolean {\n return allowedTypes.some(t => {\n if (t === 'null') {\n return provided === null;\n } else if (t === 'array') {\n return Array.isArray(provided);\n } else if (t === 'object') {\n return provided && !Array.isArray(provided) && typeof provided === 'object';\n } else {\n return t === typeof provided;\n }\n });\n}\n\nexport function typeEquals(a: Type, b: Type): boolean {\n if (a.kind === 'array' && b.kind === 'array') {\n return a.N === b.N && typeEquals(a.itemType, b.itemType);\n } else {\n return a.kind === b.kind;\n }\n}\n","// (c) Dean McNamee <dean@gmail.com>, 2012.\n//\n// https://github.com/deanm/css-color-parser-js\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to\n// deal in the Software without restriction, including without limitation the\n// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n// sell copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n// IN THE SOFTWARE.\n\n// http://www.w3.org/TR/css3-color/\nvar kCSSColorTable = {\n \"transparent\": [0,0,0,0], \"aliceblue\": [240,248,255,1],\n \"antiquewhite\": [250,235,215,1], \"aqua\": [0,255,255,1],\n \"aquamarine\": [127,255,212,1], \"azure\": [240,255,255,1],\n \"beige\": [245,245,220,1], \"bisque\": [255,228,196,1],\n \"black\": [0,0,0,1], \"blanchedalmond\": [255,235,205,1],\n \"blue\": [0,0,255,1], \"blueviolet\": [138,43,226,1],\n \"brown\": [165,42,42,1], \"burlywood\": [222,184,135,1],\n \"cadetblue\": [95,158,160,1], \"chartreuse\": [127,255,0,1],\n \"chocolate\": [210,105,30,1], \"coral\": [255,127,80,1],\n \"cornflowerblue\": [100,149,237,1], \"cornsilk\": [255,248,220,1],\n \"crimson\": [220,20,60,1], \"cyan\": [0,255,255,1],\n \"darkblue\": [0,0,139,1], \"darkcyan\": [0,139,139,1],\n \"darkgoldenrod\": [184,134,11,1], \"darkgray\": [169,169,169,1],\n \"darkgreen\": [0,100,0,1], \"darkgrey\": [169,169,169,1],\n \"darkkhaki\": [189,183,107,1], \"darkmagenta\": [139,0,139,1],\n \"darkolivegreen\": [85,107,47,1], \"darkorange\": [255,140,0,1],\n \"darkorchid\": [153,50,204,1], \"darkred\": [139,0,0,1],\n \"darksalmon\": [233,150,122,1], \"darkseagreen\": [143,188,143,1],\n \"darkslateblue\": [72,61,139,1], \"darkslategray\": [47,79,79,1],\n \"darkslategrey\": [47,79,79,1], \"darkturquoise\": [0,206,209,1],\n \"darkviolet\": [148,0,211,1], \"deeppink\": [255,20,147,1],\n \"deepskyblue\": [0,191,255,1], \"dimgray\": [105,105,105,1],\n \"dimgrey\": [105,105,105,1], \"dodgerblue\": [30,144,255,1],\n \"firebrick\": [178,34,34,1], \"floralwhite\": [255,250,240,1],\n \"forestgreen\": [34,139,34,1], \"fuchsia\": [255,0,255,1],\n \"gainsboro\": [220,220,220,1], \"ghostwhite\": [248,248,255,1],\n \"gold\": [255,215,0,1], \"goldenrod\": [218,165,32,1],\n \"gray\": [128,128,128,1], \"green\": [0,128,0,1],\n \"greenyellow\": [173,255,47,1], \"grey\": [128,128,128,1],\n \"honeydew\": [240,255,240,1], \"hotpink\": [255,105,180,1],\n \"indianred\": [205,92,92,1], \"indigo\": [75,0,130,1],\n \"ivory\": [255,255,240,1], \"khaki\": [240,230,140,1],\n \"lavender\": [230,230,250,1], \"lavenderblush\": [255,240,245,1],\n \"lawngreen\": [124,252,0,1], \"lemonchiffon\": [255,250,205,1],\n \"lightblue\": [173,216,230,1], \"lightcoral\": [240,128,128,1],\n \"lightcyan\": [224,255,255,1], \"lightgoldenrodyellow\": [250,250,210,1],\n \"lightgray\": [211,211,211,1], \"lightgreen\": [144,238,144,1],\n \"lightgrey\": [211,211,211,1], \"lightpink\": [255,182,193,1],\n \"lightsalmon\": [255,160,122,1], \"lightseagreen\": [32,178,170,1],\n \"lightskyblue\": [135,206,250,1], \"lightslategray\": [119,136,153,1],\n \"lightslategrey\": [119,136,153,1], \"lightsteelblue\": [176,196,222,1],\n \"lightyellow\": [255,255,224,1], \"lime\": [0,255,0,1],\n \"limegreen\": [50,205,50,1], \"linen\": [250,240,230,1],\n \"magenta\": [255,0,255,1], \"maroon\": [128,0,0,1],\n \"mediumaquamarine\": [102,205,170,1], \"mediumblue\": [0,0,205,1],\n \"mediumorchid\": [186,85,211,1], \"mediumpurple\": [147,112,219,1],\n \"mediumseagreen\": [60,179,113,1], \"mediumslateblue\": [123,104,238,1],\n \"mediumspringgreen\": [0,250,154,1], \"mediumturquoise\": [72,209,204,1],\n \"mediumvioletred\": [199,21,133,1], \"midnightblue\": [25,25,112,1],\n \"mintcream\": [245,255,250,1], \"mistyrose\": [255,228,225,1],\n \"moccasin\": [255,228,181,1], \"navajowhite\": [255,222,173,1],\n \"navy\": [0,0,128,1], \"oldlace\": [253,245,230,1],\n \"olive\": [128,128,0,1], \"olivedrab\": [107,142,35,1],\n \"orange\": [255,165,0,1], \"orangered\": [255,69,0,1],\n \"orchid\": [218,112,214,1], \"palegoldenrod\": [238,232,170,1],\n \"palegreen\": [152,251,152,1], \"paleturquoise\": [175,238,238,1],\n \"palevioletred\": [219,112,147,1], \"papayawhip\": [255,239,213,1],\n \"peachpuff\": [255,218,185,1], \"peru\": [205,133,63,1],\n \"pink\": [255,192,203,1], \"plum\": [221,160,221,1],\n \"powderblue\": [176,224,230,1], \"purple\": [128,0,128,1],\n \"rebeccapurple\": [102,51,153,1],\n \"red\": [255,0,0,1], \"rosybrown\": [188,143,143,1],\n \"royalblue\": [65,105,225,1], \"saddlebrown\": [139,69,19,1],\n \"salmon\": [250,128,114,1], \"sandybrown\": [244,164,96,1],\n \"seagreen\": [46,139,87,1], \"seashell\": [255,245,238,1],\n \"sienna\": [160,82,45,1], \"silver\": [192,192,192,1],\n \"skyblue\": [135,206,235,1], \"slateblue\": [106,90,205,1],\n \"slategray\": [112,128,144,1], \"slategrey\": [112,128,144,1],\n \"snow\": [255,250,250,1], \"springgreen\": [0,255,127,1],\n \"steelblue\": [70,130,180,1], \"tan\": [210,180,140,1],\n \"teal\": [0,128,128,1], \"thistle\": [216,191,216,1],\n \"tomato\": [255,99,71,1], \"turquoise\": [64,224,208,1],\n \"violet\": [238,130,238,1], \"wheat\": [245,222,179,1],\n \"white\": [255,255,255,1], \"whitesmoke\": [245,245,245,1],\n \"yellow\": [255,255,0,1], \"yellowgreen\": [154,205,50,1]}\n\nfunction clamp_css_byte(i) { // Clamp to integer 0 .. 255.\n i = Math.round(i); // Seems to be what Chrome does (vs truncation).\n return i < 0 ? 0 : i > 255 ? 255 : i;\n}\n\nfunction clamp_css_float(f) { // Clamp to float 0.0 .. 1.0.\n return f < 0 ? 0 : f > 1 ? 1 : f;\n}\n\nfunction parse_css_int(str) { // int or percentage.\n if (str[str.length - 1] === '%')\n return clamp_css_byte(parseFloat(str) / 100 * 255);\n return clamp_css_byte(parseInt(str));\n}\n\nfunction parse_css_float(str) { // float or percentage.\n if (str[str.length - 1] === '%')\n return clamp_css_float(parseFloat(str) / 100);\n return clamp_css_float(parseFloat(str));\n}\n\nfunction css_hue_to_rgb(m1, m2, h) {\n if (h < 0) h += 1;\n else if (h > 1) h -= 1;\n\n if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;\n if (h * 2 < 1) return m2;\n if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6;\n return m1;\n}\n\nfunction parseCSSColor(css_str) {\n // Remove all whitespace, not compliant, but should just be more accepting.\n var str = css_str.replace(/ /g, '').toLowerCase();\n\n // Color keywords (and transparent) lookup.\n if (str in kCSSColorTable) return kCSSColorTable[str].slice(); // dup.\n\n // #abc and #abc123 syntax.\n if (str[0] === '#') {\n if (str.length === 4) {\n var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.\n if (!(iv >= 0 && iv <= 0xfff)) return null; // Covers NaN.\n return [((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8),\n (iv & 0xf0) | ((iv & 0xf0) >> 4),\n (iv & 0xf) | ((iv & 0xf) << 4),\n 1];\n } else if (str.length === 7) {\n var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.\n if (!(iv >= 0 && iv <= 0xffffff)) return null; // Covers NaN.\n return [(iv & 0xff0000) >> 16,\n (iv & 0xff00) >> 8,\n iv & 0xff,\n 1];\n }\n\n return null;\n }\n\n var op = str.indexOf('('), ep = str.indexOf(')');\n if (op !== -1 && ep + 1 === str.length) {\n var fname = str.substr(0, op);\n var params = str.substr(op+1, ep-(op+1)).split(',');\n var alpha = 1; // To allow case fallthrough.\n switch (fname) {\n case 'rgba':\n if (params.length !== 4) return null;\n alpha = parse_css_float(params.pop());\n // Fall through.\n case 'rgb':\n if (params.length !== 3) return null;\n return [parse_css_int(params[0]),\n parse_css_int(params[1]),\n parse_css_int(params[2]),\n alpha];\n case 'hsla':\n if (params.length !== 4) return null;\n alpha = parse_css_float(params.pop());\n // Fall through.\n case 'hsl':\n if (params.length !== 3) return null;\n var h = (((parseFloat(params[0]) % 360) + 360) % 360) / 360; // 0 .. 1\n // NOTE(deanm): According to the CSS spec s/l should only be\n // percentages, but we don't bother and let float or percentage.\n var s = parse_css_float(params[1]);\n var l = parse_css_float(params[2]);\n var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;\n var m1 = l * 2 - m2;\n return [clamp_css_byte(css_hue_to_rgb(m1, m2, h+1/3) * 255),\n clamp_css_byte(css_hue_to_rgb(m1, m2, h) * 255),\n clamp_css_byte(css_hue_to_rgb(m1, m2, h-1/3) * 255),\n alpha];\n default:\n return null;\n }\n }\n\n return null;\n}\n\ntry { exports.parseCSSColor = parseCSSColor } catch(e) { }\n","import Color from './color';\n\nexport function number(a: number, b: number, t: number): number {\n return (a * (1 - t)) + (b * t);\n}\n\nexport function color(from: Color, to: Color, t: number): Color {\n return new Color(\n number(from.r, to.r, t),\n number(from.g, to.g, t),\n number(from.b, to.b, t),\n number(from.a, to.a, t)\n );\n}\n\nexport function array(from: Array<number>, to: Array<number>, t: number): Array<number> {\n return from.map((d, i) => {\n return number(d, to[i], t);\n });\n}\n","import {parseCSSColor} from 'csscolorparser';\nimport {number as lerp} from './interpolate';\n\nimport type {LUT} from '../types/lut';\n\n/**\n * An RGBA color value. Create instances from color strings using the static\n * method `Color.parse`. The constructor accepts RGB channel values in the range\n * `[0, 1]`, premultiplied by A.\n *\n * @param {number} r The red channel.\n * @param {number} g The green channel.\n * @param {number} b The blue channel.\n * @param {number} a The alpha channel.\n * @private\n */\nclass Color {\n r: number;\n g: number;\n b: number;\n a: number;\n\n constructor(r: number, g: number, b: number, a: number = 1) {\n this.r = r;\n this.g = g;\n this.b = b;\n this.a = a;\n }\n\n static black: Color;\n static white: Color;\n static transparent: Color;\n static red: Color;\n static blue: Color;\n\n /**\n * Parses valid CSS color strings and returns a `Color` instance.\n * @returns A `Color` instance, or `undefined` if the input is not a valid color string.\n */\n static parse(input?: string | Color | null): Color | undefined {\n if (!input) {\n return undefined;\n }\n\n if (input instanceof Color) {\n return input;\n }\n\n if (typeof input !== 'string') {\n return undefined;\n }\n\n const rgba = parseCSSColor(input);\n if (!rgba) {\n return undefined;\n }\n\n return new Color(\n rgba[0] / 255,\n rgba[1] / 255,\n rgba[2] / 255,\n rgba[3]\n );\n }\n\n /**\n * Returns an RGBA string representing the color value.\n *\n * @returns An RGBA string.\n * @example\n * var purple = new Color.parse('purple');\n * purple.toString; // = \"rgba(128,0,128,1)\"\n * var translucentGreen = new Color.parse('rgba(26, 207, 26, .73)');\n * translucentGreen.toString(); // = \"rgba(26,207,26,0.73)\"\n */\n toString(): string {\n const [r, g, b, a] = [\n this.r,\n this.g,\n this.b,\n this.a\n ];\n return `rgba(${Math.round(r * 255)},${Math.round(g * 255)},${Math.round(b * 255)},${a})`;\n }\n\n toNonPremultipliedRenderColor(lut: LUT | null): NonPremultipliedRenderColor {\n const {r, g, b, a} = this;\n return new NonPremultipliedRenderColor(lut, r, g, b, a);\n }\n\n toPremultipliedRenderColor(lut: LUT | null): NonPremultipliedRenderColor {\n const {r, g, b, a} = this;\n return new PremultipliedRenderColor(lut, r * a, g * a, b * a, a);\n }\n\n clone(): Color {\n return new Color(this.r, this.g, this.b, this.a);\n }\n}\n\nexport abstract class RenderColor {\n premultiplied: boolean = false;\n\n r: number;\n g: number;\n b: number;\n a: number;\n\n constructor(lut: LUT | null, r: number, g: number, b: number, a: number, premultiplied: boolean = false) {\n this.premultiplied = premultiplied;\n if (!lut) {\n this.r = r;\n this.g = g;\n this.b = b;\n this.a = a;\n } else {\n const N = lut.image.height;\n const N2 = N * N;\n\n // Normalize to cube dimensions.\n\n if (this.premultiplied) {\n r = a === 0 ? 0 : (r / a) * (N - 1);\n g = a === 0 ? 0 : (g / a) * (N - 1);\n b = a === 0 ? 0 : (b / a) * (N - 1);\n } else {\n r = r * (N - 1);\n g = g * (N - 1);\n b = b * (N - 1);\n }\n\n // Determine boundary values for the cube the color is in.\n const r0 = Math.floor(r);\n const g0 = Math.floor(g);\n const b0 = Math.floor(b);\n const r1 = Math.ceil(r);\n const g1 = Math.ceil(g);\n const b1 = Math.ceil(b);\n\n // Determine weights within the cube.\n const rw = r - r0;\n const gw = g - g0;\n const bw = b - b0;\n\n const data = lut.image.data;\n const i0 = (r0 + g0 * N2 + b0 * N) * 4;\n const i1 = (r0 + g0 * N2 + b1 * N) * 4;\n const i2 = (r0 + g1 * N2 + b0 * N) * 4;\n const i3 = (r0 + g1 * N2 + b1 * N) * 4;\n const i4 = (r1 + g0 * N2 + b0 * N) * 4;\n const i5 = (r1 + g0 * N2 + b1 * N) * 4;\n const i6 = (r1 + g1 * N2 + b0 * N) * 4;\n const i7 = (r1 + g1 * N2 + b1 * N) * 4;\n if (i0 < 0 || i7 >= data.length) {\n throw new Error(\"out of range\");\n }\n\n // Trilinear interpolation.\n this.r = lerp(\n lerp(\n lerp(data[i0], data[i1], bw),\n lerp(data[i2], data[i3], bw), gw),\n lerp(\n lerp(data[i4], data[i5], bw),\n lerp(data[i6], data[i7], bw), gw), rw) / 255 * (this.premultiplied ? a : 1);\n this.g = lerp(\n lerp(\n lerp(data[i0 + 1], data[i1 + 1], bw),\n lerp(data[i2 + 1], data[i3 + 1], bw), gw),\n lerp(\n lerp(data[i4 + 1], data[i5 + 1], bw),\n lerp(data[i6 + 1], data[i7 + 1], bw), gw), rw) / 255 * (this.premultiplied ? a : 1);\n this.b = lerp(\n lerp(\n lerp(data[i0 + 2], data[i1 + 2], bw),\n lerp(data[i2 + 2], data[i3 + 2], bw), gw),\n lerp(\n lerp(data[i4 + 2], data[i5 + 2], bw),\n lerp(data[i6 + 2], data[i7 + 2], bw), gw), rw) / 255 * (this.premultiplied ? a : 1);\n this.a = a;\n }\n }\n\n /**\n * Returns an RGBA array of values representing the color.\n * @returns An array of RGBA color values in the range [0, 255].\n */\n toArray(): [number, number, number, number] {\n const {r, g, b, a} = this;\n\n return [\n r * 255,\n g * 255,\n b * 255,\n a\n ];\n\n }\n\n /**\n * Returns an HSLA array of values representing the color, unpremultiplied by A.\n * @returns An array of HSLA color values.\n */\n toHslaArray(): [number, number, number, number] {\n let {r, g, b, a} = this;\n\n if (this.premultiplied) {\n if (a === 0) return [0, 0, 0, 0];\n\n r /= a;\n g /= a;\n b /= a;\n }\n\n const red = Math.min(Math.max(r, 0.0), 1.0);\n const green = Math.min(Math.max(g, 0.0), 1.0);\n const blue = Math.min(Math.max(b, 0.0), 1.0);\n\n const min = Math.min(red, green, blue);\n const max = Math.max(red, green, blue);\n\n const l = (min + max) / 2;\n\n if (min === max) {\n return [0, 0, l * 100, a];\n }\n\n const delta = max - min;\n\n const s = l > 0.5 ? delta / (2 - max - min) : delta / (max + min);\n\n let h = 0;\n if (max === red) {\n h = (green - blue) / delta + (green < blue ? 6 : 0);\n } else if (max === green) {\n h = (blue - red) / delta + 2;\n } else if (max === blue) {\n h = (red - green) / delta + 4;\n }\n\n h *= 60;\n\n return [\n Math.min(Math.max(h, 0), 360),\n Math.min(Math.max(s * 100, 0), 100),\n Math.min(Math.max(l * 100, 0), 100),\n a\n ];\n }\n\n /**\n * Returns a RGBA array of float values representing the color.\n *\n * @returns An array of RGBA color values in the range [0, 1].\n */\n toArray01(): [number, number, number, number] {\n const {r, g, b, a} = this;\n\n return [\n r,\n g,\n b,\n a\n ];\n }\n\n /**\n * Returns an RGB array of values representing the color, unpremultiplied by A and multiplied by a scalar.\n *\n * @param {number} scale A scale to apply to the unpremultiplied-alpha values.\n * @returns An array of RGB color values in the range [0, 1].\n */\n toArray01Scaled(scale: number): [number, number, number] {\n const {r, g, b} = this;\n\n return [\n r * scale,\n g * scale,\n b * scale\n ];\n }\n\n /**\n * Returns an RGBA array of values representing the color converted to linear color space.\n * The color is defined by sRGB primaries, but the sRGB transfer function\n * is reversed to obtain linear energy.\n * @returns An array of RGBA color values in the range [0, 1].\n */\n toArray01Linear(): [number, number, number, number] {\n const {r, g, b, a} = this;\n\n return [\n Math.pow(r, 2.2),\n Math.pow(g, 2.2),\n Math.pow(b, 2.2),\n a\n ];\n }\n}\n\n/**\n * Renderable color created from a Color and an optional LUT value.\n * Represent a color value with non-premultiplied alpha.\n */\nexport class NonPremultipliedRenderColor extends RenderColor {\n constructor(lut: LUT | null, r: number, g: number, b: number, a: number) {\n super(lut, r, g, b, a, false);\n }\n}\n\n/**\n * Renderable color created from a Color and an optional LUT value.\n * Represent a color value with premultiplied alpha.\n */\nexport class PremultipliedRenderColor extends RenderColor {\n constructor(lut: LUT | null, r: number, g: number, b: number, a: number) {\n super(lut, r, g, b, a, true);\n }\n}\n\nColor.black = new Color(0, 0, 0, 1);\nColor.white = new Color(1, 1, 1, 1);\nColor.transparent = new Color(0, 0, 0, 0);\nColor.red = new Color(1, 0, 0, 1);\nColor.blue = new Color(0, 0, 1, 1);\n\nexport default Color;\n","export default class Collator {\n locale: string | null;\n sensitivity: 'base' | 'accent' | 'case' | 'variant';\n collator: Intl.Collator;\n\n constructor(caseSensitive: boolean, diacriticSensitive: boolean, locale: string | null) {\n if (caseSensitive)\n this.sensitivity = diacriticSensitive ? 'variant' : 'case';\n else\n this.sensitivity = diacriticSensitive ? 'accent' : 'base';\n\n this.locale = locale;\n this.collator = new Intl.Collator(this.locale ? this.locale : [],\n {sensitivity: this.sensitivity, usage: 'search'});\n }\n\n compare(lhs: string, rhs: string): number {\n return this.collator.compare(lhs, rhs);\n }\n\n resolvedLocale(): string {\n // We create a Collator without \"usage: search\" because we don't want\n // the search options encoded in our result (e.g. \"en-u-co-search\")\n return new Intl.Collator(this.locale ? this.locale : [])\n .resolvedOptions().locale;\n }\n}\n","import type Color from '../../util/color';\nimport type ResolvedImage from '../types/resolved_image';\n\nexport class FormattedSection {\n text: string;\n image: ResolvedImage | null;\n scale: number | null;\n fontStack: string | null;\n textColor: Color | null;\n\n constructor(text: string, image: ResolvedImage | null, scale: number | null, fontStack: string | null, textColor: Color | null) {\n // combine characters so that diacritic marks are not separate code points\n this.text = text.normalize ? text.normalize() : text;\n this.image = image;\n this.scale = scale;\n this.fontStack = fontStack;\n this.textColor = textColor;\n }\n}\n\nexport default class Formatted {\n sections: Array<FormattedSection>;\n\n constructor(sections: Array<FormattedSection>) {\n this.sections = sections;\n }\n\n static fromString(unformatted: string): Formatted {\n return new Formatted([new FormattedSection(unformatted, null, null, null, null)]);\n }\n\n isEmpty(): boolean {\n if (this.sections.length === 0) return true;\n return !this.sections.some(section => {\n if (section.text.length !== 0) return true;\n if (!section.image) return false;\n return section.image.hasPrimary();\n });\n }\n\n static factory(text: Formatted | string): Formatted {\n if (text instanceof Formatted) {\n return text;\n } else {\n return Formatted.fromString(text);\n }\n }\n\n toString(): string {\n if (this.sections.length === 0) return '';\n return this.sections.map(section => section.text).join('');\n }\n\n serialize(): Array<unknown> {\n const serialized: Array<unknown> = [\"format\"];\n for (const section of this.sections) {\n if (section.image) {\n const primaryId = section.image.getPrimary().id.toString();\n serialized.push(['image', primaryId]);\n continue;\n }\n serialized.push(section.text);\n const options: {\n [key: string]: unknown;\n } = {};\n if (section.fontStack) {\n options[\"text-font\"] = [\"literal\", section.fontStack.split(',')];\n }\n if (section.scale) {\n options[\"font-scale\"] = section.scale;\n }\n if (section.textColor) {\n options[\"text-color\"] = ([\"rgba\"] as Array<unknown>).concat(section.textColor.toNonPremultipliedRenderColor(null).toArray());\n }\n serialized.push(options);\n }\n return serialized;\n }\n}\n","import type {Brand} from '../../types/brand';\n\nconst separator = '\\u001F';\n\nexport type ImageIdSpec = {\n name: string;\n iconsetId?: string;\n};\n\n/**\n * `StringifiedImageId` is a stringified version of the `ImageId`.\n *\n * @private\n */\nexport type StringifiedImageId = Brand<string, 'ImageId'>;\n\n/**\n * `ImageId` is a reference to an {@link ImageVariant} in the sprite or iconset.\n *\n * @private\n */\nexport class ImageId {\n name: string;\n iconsetId?: string;\n\n constructor(id: string | ImageId | ImageIdSpec) {\n if (typeof id === 'string') {\n this.name = id;\n } else {\n this.name = id.name;\n this.iconsetId = id.iconsetId;\n }\n }\n\n static from(id: string | ImageId | ImageIdSpec): ImageId {\n return new ImageId(id);\n }\n\n static toString(id: ImageId | ImageIdSpec): StringifiedImageId {\n return (id.iconsetId ? `${id.name}${separator}${id.iconsetId}` : id.name) as StringifiedImageId;\n }\n\n static parse(str: StringifiedImageId): ImageId | null {\n const [name, iconsetId] = str.split(separator);\n return new ImageId({name, iconsetId});\n }\n\n static isEqual(a: ImageId | ImageIdSpec, b: ImageId | ImageIdSpec): boolean {\n return a.name === b.name && a.iconsetId === b.iconsetId;\n }\n\n toString(): StringifiedImageId {\n return ImageId.toString(this);\n }\n\n serialize(): ImageIdSpec {\n return {name: this.name, iconsetId: this.iconsetId};\n }\n}\n","import {ImageId} from './image_id';\n\nimport type Color from '../../util/color';\nimport type {Brand} from '../../types/brand';\nimport type {ImageIdSpec} from './image_id';\n\n/**\n * `StringifiedImageVariant` is a stringified version of the `ImageVariant`.\n *\n * @private\n */\nexport type StringifiedImageVariant = Brand<string, 'ImageVariant'>;\n\n/**\n * {@link ImageVariant} rasterization options.\n *\n * @private\n */\nexport type RasterizationOptions = {\n params?: Record<string, Color>;\n transform?: DOMMatrix;\n}\n\n/**\n * `ImageVariant` is a component of {@link ResolvedImage}\n * that represents either the primary or secondary image\n * along with its rendering c