@icones/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +72 -0
  2. package/RUNTIME.md +81 -0
  3. package/UTILITIES.md +30 -0
  4. package/dist/catalog.d.ts +17 -0
  5. package/dist/catalog.js +115 -0
  6. package/dist/controller.d.ts +27 -0
  7. package/dist/controller.js +156 -0
  8. package/dist/data-DtdeiZ5o.d.ts +18 -0
  9. package/dist/data-N59s4mFY.js +189 -0
  10. package/dist/data.d.ts +4 -0
  11. package/dist/data.js +36 -0
  12. package/dist/element-types-C9jOyda4.d.ts +9 -0
  13. package/dist/element-types.d.ts +2 -0
  14. package/dist/element-types.js +0 -0
  15. package/dist/elements-DLSCanEZ.d.ts +16 -0
  16. package/dist/elements.d.ts +3 -0
  17. package/dist/elements.js +29 -0
  18. package/dist/icon-data-HGvJGwoB.d.ts +42 -0
  19. package/dist/icon-data.d.ts +2 -0
  20. package/dist/icon-data.js +95 -0
  21. package/dist/index.d.ts +17 -0
  22. package/dist/index.js +15 -0
  23. package/dist/loaders-DxxA-L6t.d.ts +58 -0
  24. package/dist/loaders.d.ts +2 -0
  25. package/dist/loaders.js +112 -0
  26. package/dist/manifest.d.ts +9 -0
  27. package/dist/manifest.js +87 -0
  28. package/dist/presentation.d.ts +94 -0
  29. package/dist/presentation.js +189 -0
  30. package/dist/registry.d.ts +25 -0
  31. package/dist/registry.js +66 -0
  32. package/dist/resource-types.d.ts +68 -0
  33. package/dist/resource-types.js +0 -0
  34. package/dist/runtime.d.ts +7 -0
  35. package/dist/runtime.js +12 -0
  36. package/dist/set-options-C2BMGh-r.d.ts +11 -0
  37. package/dist/set-options.d.ts +2 -0
  38. package/dist/set-options.js +23 -0
  39. package/dist/sizes-CMxyEIpX.d.ts +10 -0
  40. package/dist/sizes.d.ts +2 -0
  41. package/dist/sizes.js +0 -0
  42. package/dist/slug.d.ts +5 -0
  43. package/dist/slug.js +7 -0
  44. package/dist/sources-CXcG9DjB.d.ts +11 -0
  45. package/dist/store.d.ts +36 -0
  46. package/dist/store.js +265 -0
  47. package/dist/svg-data.d.ts +2 -0
  48. package/dist/svg-data.js +2 -0
  49. package/dist/svg.d.ts +19 -0
  50. package/dist/svg.js +49 -0
  51. package/dist/symbol.d.ts +7 -0
  52. package/dist/symbol.js +22 -0
  53. package/dist/types.d.ts +30 -0
  54. package/dist/types.js +0 -0
  55. package/dist/view-box.d.ts +6 -0
  56. package/dist/view-box.js +9 -0
  57. package/package.json +133 -0
@@ -0,0 +1,112 @@
1
+ import { isIconData, isIconSet, readIconSet } from "./icon-data.js";
2
+ import { isElementData } from "./elements.js";
3
+ import { parseIconName, resolveIconSetData } from "./data.js";
4
+ import { mergeSetOptions, resolveSetOption } from "./set-options.js";
5
+ //#region src/runtime/loaders.ts
6
+ /** Default first-party static icon service used by the framework adapters. */
7
+ const defaultIconServiceDomain = "icones.go-slim.dev";
8
+ /** Determine whether an API config is a shared map or a direct API value. */
9
+ function isIconApiMap(api) {
10
+ if (typeof api !== "object" || api === null) return false;
11
+ if (Object.hasOwn(api, "default")) return true;
12
+ return Object.entries(api).some(([key, value]) => {
13
+ switch (key) {
14
+ case "type": return value !== void 0 && value !== "fetch" && value !== "symbol";
15
+ case "baseUrl":
16
+ case "viewBox": return value !== void 0 && typeof value !== "string";
17
+ case "url":
18
+ case "fetch":
19
+ case "transform": return value !== void 0 && typeof value !== "function";
20
+ case "requestInit": return value !== void 0 && (typeof value !== "object" || value === null);
21
+ default: return true;
22
+ }
23
+ });
24
+ }
25
+ function mergeIconApi(parent, next) {
26
+ return mergeSetOptions(parent, next, isIconApiMap);
27
+ }
28
+ function resolveIconApi(api, name) {
29
+ return resolveSetOption(api, parseIconName(name)?.prefix, isIconApiMap);
30
+ }
31
+ /** Check whether a resolved API entry points to an external symbol sprite. */
32
+ function isSymbolApi(api) {
33
+ return typeof api === "object" && api.type === "symbol";
34
+ }
35
+ /** Build a fetch-based icon loader from API option object, URL or callback. */
36
+ function createIconApiLoader(api = false) {
37
+ if (api === false) return () => null;
38
+ if (typeof api === "function") return api;
39
+ const options = typeof api === "string" ? { baseUrl: api } : api;
40
+ const baseUrl = trimTrailingSlash(options.baseUrl ?? "/icons");
41
+ return async (name, parsed, request) => {
42
+ if (!options.url && (!name.includes(":") || !parsed || parsed.provider)) return null;
43
+ const resolved = parsed ?? parseIconName(name);
44
+ const url = options.url ? options.url(name, parsed) : `${baseUrl}/${encodeURIComponent(resolved.prefix)}.json?icons=${encodeURIComponent(resolved.name)}`;
45
+ const response = await (options.fetch ?? globalThis.fetch)(url, {
46
+ ...options.requestInit,
47
+ signal: request?.signal && options.requestInit?.signal ? AbortSignal.any([request.signal, options.requestInit.signal]) : request?.signal ?? options.requestInit?.signal
48
+ });
49
+ if (response.status === 404) return null;
50
+ if (!response.ok) throw new Error(`Unable to load icon ${name} (${response.status}).`);
51
+ const json = await response.json();
52
+ const result = options.transform ? await options.transform(json, name) : parseLoaderResult(json, name);
53
+ return !options.url && isIconSet(result) ? resolveIconSetData(result, name) : result;
54
+ };
55
+ }
56
+ /** Opt in to mirrored static assets: <base>/<set>/data/<slug>.json. */
57
+ function createStaticIconLoader(options = {}) {
58
+ const normalized = typeof options === "string" ? { baseUrl: options } : options;
59
+ const base = trimTrailingSlash(normalized.baseUrl ?? "/icons");
60
+ const load = createIconApiLoader({
61
+ ...normalized,
62
+ url: (_name, parsed) => `${base}/${encodeURIComponent(parsed.prefix)}/data/${encodeURIComponent(parsed.name)}.json`
63
+ });
64
+ return (name, parsed, request) => {
65
+ const resolved = parsed ?? parseIconName(name);
66
+ const segment = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
67
+ if (!name.includes(":") || !resolved || resolved.provider || !segment.test(resolved.prefix) || !segment.test(resolved.name)) return null;
68
+ return load(name, resolved, request);
69
+ };
70
+ }
71
+ /** Load <set>:<name> from https://<set>.<domain>/data/<name>.json. */
72
+ function createIconesIconLoader(options = {}) {
73
+ const domain = options.domain ?? "icones.go-slim.dev";
74
+ if (domain.length > 253 || !domain.includes(".") || domain.split(".").some((label) => !/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label))) throw new TypeError(`Invalid icon service domain: ${domain}`);
75
+ const load = createIconApiLoader({
76
+ fetch: options.fetch,
77
+ requestInit: options.requestInit,
78
+ url: (_name, parsed) => `https://${parsed.prefix}.${domain}/data/${encodeURIComponent(parsed.name)}.json`
79
+ });
80
+ return (name, parsed, request) => {
81
+ const resolved = parsed ?? parseIconName(name);
82
+ const segment = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
83
+ if (!name.includes(":") || !resolved || resolved.provider || !segment.test(resolved.prefix) || !segment.test(resolved.name)) return null;
84
+ return load(name, resolved, request);
85
+ };
86
+ }
87
+ function createFileIconLoader(options = {}) {
88
+ const normalizedOptions = typeof options === "string" ? { baseUrl: options } : options;
89
+ const baseUrl = trimTrailingSlash(normalizedOptions.baseUrl ?? "/icons");
90
+ const extension = normalizedOptions.extension ?? ".json";
91
+ const request = normalizedOptions.fetch ?? globalThis.fetch;
92
+ return async (name, _parsed, context) => {
93
+ const response = await request(`${baseUrl}/${encodeURIComponent(name)}${extension}`, { signal: context?.signal });
94
+ if (response.status === 404) return null;
95
+ if (!response.ok) throw new Error(`Unable to load icon ${name} (${response.status}).`);
96
+ return parseLoaderResult(await response.json(), name);
97
+ };
98
+ }
99
+ /** Unconfigured adapters fall back to the first-party per-collection service. */
100
+ const defaultIconLoader = createIconesIconLoader();
101
+ /** Validate file/network responses and keep only supported payload shapes. */
102
+ function parseLoaderResult(value, name) {
103
+ if (isElementData(value)) return value;
104
+ if (isIconData(value)) return value;
105
+ if (isIconSet(value)) return readIconSet(value);
106
+ throw new Error(`Icon data file for ${name} is invalid.`);
107
+ }
108
+ function trimTrailingSlash(value) {
109
+ return value.replace(/\/+$/, "");
110
+ }
111
+ //#endregion
112
+ export { createFileIconLoader, createIconApiLoader, createIconesIconLoader, createStaticIconLoader, defaultIconLoader, defaultIconServiceDomain, isSymbolApi, mergeIconApi, resolveIconApi };
@@ -0,0 +1,9 @@
1
+ import { IconManifest, IconSource, ManifestEntry } from "./resource-types.js";
2
+ //#region src/resources/manifest.d.ts
3
+ declare function readIconManifest(value: unknown, prefix?: string): IconManifest;
4
+ declare function manifestEntries(manifest: IconManifest): ManifestEntry[];
5
+ declare function createIconManifest(prefix: string): IconManifest;
6
+ declare function addManifestEntry(manifest: IconManifest, entry: ManifestEntry): void;
7
+ declare function serializeManifest(manifest: IconManifest, variantOrder?: readonly string[]): string;
8
+ //#endregion
9
+ export { type IconManifest, type IconSource, type ManifestEntry, addManifestEntry, createIconManifest, manifestEntries, readIconManifest, serializeManifest };
@@ -0,0 +1,87 @@
1
+ //#region src/resources/manifest.ts
2
+ const segment = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
3
+ const object = (value) => !!value && typeof value === "object" && !Array.isArray(value);
4
+ function readIconManifest(value, prefix) {
5
+ if (!object(value) || value.version !== 1 || typeof value.prefix !== "string" || !segment.test(value.prefix) || prefix !== void 0 && value.prefix !== prefix || !object(value.variants)) throw new TypeError(`Invalid icon manifest: ${prefix ?? "unknown"}`);
6
+ const names = /* @__PURE__ */ new Set();
7
+ if (value.variantAliases !== void 0) {
8
+ if (!object(value.variantAliases) || Object.entries(value.variantAliases).some(([variant, alias]) => !["outline", "solid"].includes(variant) || typeof alias !== "string" || !segment.test(alias)) || new Set(Object.values(value.variantAliases)).size !== Object.keys(value.variantAliases).length) throw new TypeError("Invalid manifest variant aliases.");
9
+ }
10
+ for (const [variant, categories] of Object.entries(value.variants)) {
11
+ if (!segment.test(variant) || !object(categories)) throw new TypeError("Invalid manifest variant.");
12
+ for (const [category, files] of Object.entries(categories)) {
13
+ if (!segment.test(category) || !object(files) || !Array.isArray(files.json) || !Array.isArray(files.svg)) throw new TypeError("Invalid manifest category.");
14
+ const json = files.json;
15
+ const svg = files.svg;
16
+ if (json.length !== svg.length) throw new TypeError("Manifest JSON/SVG inventory must match.");
17
+ const symbols = new Set(svg);
18
+ for (const file of json) {
19
+ if (typeof file !== "string" || !file.endsWith(".json") || !segment.test(file.slice(0, -5))) throw new TypeError("Manifest filenames must be flat icon slugs.");
20
+ const slug = file.slice(0, -5);
21
+ if (names.has(slug)) throw new TypeError(`Duplicate manifest icon: ${value.prefix}:${slug}`);
22
+ if (!symbols.delete(slug + ".svg")) throw new TypeError(`Missing manifest symbol: ${slug}`);
23
+ names.add(slug);
24
+ }
25
+ if (symbols.size) throw new TypeError("Invalid manifest symbol inventory.");
26
+ }
27
+ }
28
+ if (value.aliases !== void 0) {
29
+ if (!object(value.aliases)) throw new TypeError("Invalid manifest aliases.");
30
+ for (const [alias, rule] of Object.entries(value.aliases)) if (!segment.test(alias) || alias === value.prefix || !object(rule) || typeof rule.suffix !== "string" || rule.suffix !== "" && !/^-[a-z0-9]+(?:-[a-z0-9]+)*$/.test(rule.suffix)) throw new TypeError("Invalid manifest alias rule.");
31
+ }
32
+ if (value.sources !== void 0 && (!object(value.sources) || Object.entries(value.sources).some(([key, source]) => !segment.test(key) || !object(source) || typeof source.url !== "string"))) throw new TypeError("Invalid manifest sources.");
33
+ return value;
34
+ }
35
+ function manifestEntries(manifest) {
36
+ return Object.entries(manifest.variants).flatMap(([variant, categories]) => Object.entries(categories).flatMap(([category, files]) => files.json.map((file) => ({
37
+ prefix: manifest.prefix,
38
+ slug: file.slice(0, -5),
39
+ variant,
40
+ ...Object.hasOwn(manifest.variantAliases ?? {}, variant) ? { variantAlias: manifest.variantAliases[variant] } : {},
41
+ category
42
+ }))));
43
+ }
44
+ function createIconManifest(prefix) {
45
+ return {
46
+ version: 1,
47
+ prefix,
48
+ variants: Object.create(null)
49
+ };
50
+ }
51
+ function addManifestEntry(manifest, entry) {
52
+ if (manifest.prefix !== entry.prefix || ![
53
+ entry.prefix,
54
+ entry.slug,
55
+ entry.variant,
56
+ entry.category
57
+ ].every((part) => segment.test(part)) || entry.variantAlias !== void 0 && (!["outline", "solid"].includes(entry.variant) || !segment.test(entry.variantAlias))) throw new TypeError("Invalid manifest entry.");
58
+ if (entry.variantAlias !== void 0) manifest.variantAliases = {
59
+ ...manifest.variantAliases,
60
+ [entry.variant]: entry.variantAlias
61
+ };
62
+ for (const categories of Object.values(manifest.variants)) for (const files of Object.values(categories)) {
63
+ files.json = files.json.filter((file) => file !== entry.slug + ".json");
64
+ files.svg = files.svg.filter((file) => file !== entry.slug + ".svg");
65
+ }
66
+ const categories = Object.hasOwn(manifest.variants, entry.variant) ? manifest.variants[entry.variant] : manifest.variants[entry.variant] = Object.create(null);
67
+ const files = Object.hasOwn(categories, entry.category) ? categories[entry.category] : categories[entry.category] = {
68
+ json: [],
69
+ svg: []
70
+ };
71
+ files.json.push(entry.slug + ".json");
72
+ files.svg.push(entry.slug + ".svg");
73
+ }
74
+ function serializeManifest(manifest, variantOrder = []) {
75
+ const order = variantOrder;
76
+ const variants = Object.fromEntries(Object.entries(manifest.variants).sort(([a], [b]) => (order.indexOf(a) < 0 ? 99 : order.indexOf(a)) - (order.indexOf(b) < 0 ? 99 : order.indexOf(b)) || a.localeCompare(b)).map(([variant, categories]) => [variant, Object.fromEntries(Object.entries(categories).filter(([, files]) => files.json.length).sort(([a], [b]) => a.localeCompare(b)).map(([category, files]) => [category, {
77
+ json: [...files.json].sort(),
78
+ svg: [...files.svg].sort()
79
+ }]))]).filter(([, categories]) => Object.keys(categories).length));
80
+ return JSON.stringify(readIconManifest({
81
+ ...manifest,
82
+ variants,
83
+ ...manifest.variantAliases ? { variantAliases: Object.fromEntries(Object.entries(manifest.variantAliases).sort(([a], [b]) => a.localeCompare(b))) } : {}
84
+ }), null, 2) + "\n";
85
+ }
86
+ //#endregion
87
+ export { addManifestEntry, createIconManifest, manifestEntries, readIconManifest, serializeManifest };
@@ -0,0 +1,94 @@
1
+ import { Data, IconLoadState, IconLoader, Name } from "./types.js";
2
+ import { n as IconSetOptions, t as IconSetMap } from "./set-options-C2BMGh-r.js";
3
+ import { n as DefaultSizeName, t as CSSSize } from "./sizes-CMxyEIpX.js";
4
+ //#region src/runtime/presentation.d.ts
5
+ type IconSize = DefaultSizeName | CSSSize | number;
6
+ type IconDefaultSize<Size extends string | number = IconSize> = IconSetOptions<Size>;
7
+ type IconSizePresets<Preset extends string = string> = Readonly<Partial<Record<Preset, string | number>>>;
8
+ type IconSizeValues<Preset extends string = string> = IconSetOptions<IconSizePresets<Preset>>;
9
+ type IconSourceProps = {
10
+ name: Name;
11
+ data?: never;
12
+ icon?: never;
13
+ } | {
14
+ data: Data;
15
+ name?: Name;
16
+ icon?: never;
17
+ } | {
18
+ icon: Name | Data;
19
+ name?: never;
20
+ data?: never;
21
+ };
22
+ type IconPresentation<Size extends string | number = IconSize> = {
23
+ size?: Size;
24
+ width?: string | number;
25
+ height?: string | number;
26
+ color?: string;
27
+ fill?: string;
28
+ strokeWidth?: number;
29
+ absoluteStrokeWidth?: boolean;
30
+ rotate?: number;
31
+ hFlip?: boolean;
32
+ vFlip?: boolean;
33
+ altIcon?: Name | Data;
34
+ altName?: Name;
35
+ /** Inline alternative artwork; takes priority over altName. */
36
+ altData?: Data;
37
+ showAlt?: boolean;
38
+ /** An explicit per-icon loader takes priority over collected static names. */
39
+ loader?: IconLoader;
40
+ "aria-label"?: string;
41
+ "aria-hidden"?: boolean | "true" | "false";
42
+ role?: string;
43
+ };
44
+ type IconOptions<Size extends string | number = IconSize> = IconSourceProps & IconPresentation<Size>;
45
+ type IconAppearance<Size extends string | number = IconSize> = {
46
+ /** Presets shared by all sets, or per-set presets merged over `default`. */
47
+ sizeValues?: IconSizeValues;
48
+ /** A shared size, or set-specific sizes with an optional `default` fallback. */
49
+ defaultSize?: IconDefaultSize<Size>;
50
+ strokeWidth?: IconSetOptions<number>;
51
+ absoluteStrokeWidth?: IconSetOptions<boolean>;
52
+ };
53
+ declare const defaultIconAppearance: {
54
+ readonly sizeValues: {
55
+ readonly xs: 12;
56
+ readonly sm: 16;
57
+ readonly md: 20;
58
+ readonly lg: 24;
59
+ readonly xl: 28;
60
+ };
61
+ readonly defaultSize: "md";
62
+ readonly strokeWidth: 1.5;
63
+ readonly absoluteStrokeWidth: false;
64
+ };
65
+ /** Child maps merge by set; a scalar replaces every inherited size. */
66
+ declare function mergeIconDefaultSize<Size extends string | number>(parent: IconDefaultSize<Size> | undefined, next: IconDefaultSize<Size> | undefined): IconDefaultSize<Size> | undefined;
67
+ /** Preset dictionaries merge by key, both globally and within each set. */
68
+ declare function mergeIconSizeValues(parent: IconSizeValues | undefined, next: IconSizeValues | undefined): IconSizeValues | undefined;
69
+ declare function mergeIconAppearance<Size extends string | number>(parent: IconAppearance<Size>, next: IconAppearance<Size>): IconAppearance<Size>;
70
+ /** Runtime prop names used to keep component-only options off the SVG element. */
71
+ declare const iconOptionKeys: readonly ["name", "data", "icon", "size", "width", "height", "color", "fill", "strokeWidth", "absoluteStrokeWidth", "rotate", "hFlip", "vFlip", "altIcon", "altName", "altData", "showAlt", "loader", "aria-label", "aria-hidden", "role"];
72
+ type IconSelectionProps = IconSourceProps & Pick<IconPresentation, "altIcon" | "altName" | "altData" | "showAlt">;
73
+ /** Keep selection pure: rendering the already-selected source must not warn again. */
74
+ declare function selectIconSource(props: IconSelectionProps): {
75
+ source: Data | Name;
76
+ name: Name | undefined;
77
+ };
78
+ /** Validate new data references; report each conflict once until resolved, per instance. */
79
+ declare function createIconSourceValidator(): (props: IconSelectionProps) => void;
80
+ type IconRenderResult = {
81
+ attributes: Record<string, string | number | boolean | undefined>;
82
+ body: string;
83
+ style: Record<string, string | number | undefined>;
84
+ available: boolean;
85
+ };
86
+ /**
87
+ * Pure SVG rendering shared by framework adapters.
88
+ * `instanceId` must be stable across server and client hydration.
89
+ */
90
+ declare function renderIcon(state: IconLoadState, props: IconOptions<string | number>, config?: IconAppearance<string | number>, instanceId?: string): IconRenderResult;
91
+ /** Serialize only style values; framework adapters still escape the HTML attribute. */
92
+ declare function iconStyleText(style: IconRenderResult["style"]): string;
93
+ //#endregion
94
+ export { IconAppearance, IconDefaultSize, IconOptions, IconPresentation, IconRenderResult, type IconSetMap, type IconSetOptions, IconSize, IconSizePresets, IconSizeValues, IconSourceProps, createIconSourceValidator, defaultIconAppearance, iconOptionKeys, iconStyleText, mergeIconAppearance, mergeIconDefaultSize, mergeIconSizeValues, renderIcon, selectIconSource };
@@ -0,0 +1,189 @@
1
+ import { isIconData } from "./icon-data.js";
2
+ import { n as elementDataToIcon, r as renderSvgData } from "./data-N59s4mFY.js";
3
+ import { isElementData } from "./elements.js";
4
+ import { parseIconName } from "./data.js";
5
+ import { mergeSetOptions, ownValue, resolveSetOption } from "./set-options.js";
6
+ import { configurableStrokeBody, getIconViewBox, replaceSvgIds, withIconViewBox } from "./svg.js";
7
+ //#region src/runtime/presentation.ts
8
+ const defaultIconAppearance = {
9
+ sizeValues: {
10
+ xs: 12,
11
+ sm: 16,
12
+ md: 20,
13
+ lg: 24,
14
+ xl: 28
15
+ },
16
+ defaultSize: "md",
17
+ strokeWidth: 1.5,
18
+ absoluteStrokeWidth: false
19
+ };
20
+ /** Child maps merge by set; a scalar replaces every inherited size. */
21
+ function mergeIconDefaultSize(parent, next) {
22
+ return mergeSetOptions(parent, next);
23
+ }
24
+ function sourceSet(source) {
25
+ return typeof source === "string" ? parseIconName(source)?.prefix : void 0;
26
+ }
27
+ function isSizeValuesMap(values) {
28
+ return Object.values(values).some((value) => typeof value === "object" && value !== null);
29
+ }
30
+ /** Preset dictionaries merge by key, both globally and within each set. */
31
+ function mergeIconSizeValues(parent, next) {
32
+ if (next === void 0) return parent;
33
+ if (!parent) return next;
34
+ if (!isSizeValuesMap(parent) && !isSizeValuesMap(next)) return {
35
+ ...parent,
36
+ ...next
37
+ };
38
+ const inherited = isSizeValuesMap(parent) ? parent : { default: parent };
39
+ const overrides = isSizeValuesMap(next) ? next : { default: next };
40
+ const result = { ...inherited };
41
+ for (const [set, values] of Object.entries(overrides)) if (values !== void 0) Object.defineProperty(result, set, {
42
+ value: {
43
+ ...ownValue(inherited, set),
44
+ ...values
45
+ },
46
+ enumerable: true,
47
+ configurable: true,
48
+ writable: true
49
+ });
50
+ return result;
51
+ }
52
+ function mergeIconAppearance(parent, next) {
53
+ return {
54
+ sizeValues: mergeIconSizeValues(parent.sizeValues, next.sizeValues),
55
+ defaultSize: mergeIconDefaultSize(parent.defaultSize, next.defaultSize),
56
+ strokeWidth: mergeSetOptions(parent.strokeWidth, next.strokeWidth),
57
+ absoluteStrokeWidth: mergeSetOptions(parent.absoluteStrokeWidth, next.absoluteStrokeWidth)
58
+ };
59
+ }
60
+ /** Resolve a size preset from either shared map or per-set defaults. */
61
+ function resolveSizeValue(values, size, set) {
62
+ if (!values) return void 0;
63
+ if (!isSizeValuesMap(values)) return ownValue(values, size);
64
+ return ownValue(ownValue(values, set), size) ?? ownValue(ownValue(values, "default"), size);
65
+ }
66
+ /** Runtime prop names used to keep component-only options off the SVG element. */
67
+ const iconOptionKeys = [
68
+ "name",
69
+ "data",
70
+ "icon",
71
+ "size",
72
+ "width",
73
+ "height",
74
+ "color",
75
+ "fill",
76
+ "strokeWidth",
77
+ "absoluteStrokeWidth",
78
+ "rotate",
79
+ "hFlip",
80
+ "vFlip",
81
+ "altIcon",
82
+ "altName",
83
+ "altData",
84
+ "showAlt",
85
+ "loader",
86
+ "aria-label",
87
+ "aria-hidden",
88
+ "role"
89
+ ];
90
+ /** Keep selection pure: rendering the already-selected source must not warn again. */
91
+ function selectIconSource(props) {
92
+ const alternative = props.altData ?? props.altIcon ?? props.altName;
93
+ if (props.showAlt && alternative != null) return {
94
+ source: alternative,
95
+ name: props.altData != null ? props.altName : void 0
96
+ };
97
+ return {
98
+ source: props.data ?? props.icon ?? props.name ?? "",
99
+ name: props.data != null ? props.name : void 0
100
+ };
101
+ }
102
+ function validateInlineData(value, prop) {
103
+ if (value != null && !Array.isArray(value) && !isIconData(value)) throw new TypeError(`[icones] "${prop}" must contain a single icon's data. Register collections with "sources" and select an icon with "${prop === "data" ? "name" : "altName"}".`);
104
+ }
105
+ /** Validate new data references; report each conflict once until resolved, per instance. */
106
+ function createIconSourceValidator() {
107
+ let primaryConflict = false;
108
+ let alternativeConflict = false;
109
+ let previousData;
110
+ let previousAltData;
111
+ return (props) => {
112
+ if (props.data !== previousData) validateInlineData(props.data, "data");
113
+ if (props.altData !== previousAltData) validateInlineData(props.altData, "altData");
114
+ previousData = props.data;
115
+ previousAltData = props.altData;
116
+ const primary = props.name != null && props.data != null;
117
+ const alternative = props.altName != null && props.altData != null;
118
+ if (primary && !primaryConflict) console.error("[icones] \"name\" and \"data\" were both provided. \"data\" takes priority; pass only one of them.");
119
+ if (alternative && !alternativeConflict) console.error("[icones] \"altName\" and \"altData\" were both provided. \"altData\" takes priority; pass only one of them.");
120
+ primaryConflict = primary;
121
+ alternativeConflict = alternative;
122
+ };
123
+ }
124
+ function numericLength(value) {
125
+ return typeof value === "number" ? value : /^\d+(\.\d+)?(px)?$/.test(value) ? Number.parseFloat(value) : NaN;
126
+ }
127
+ /**
128
+ * Pure SVG rendering shared by framework adapters.
129
+ * `instanceId` must be stable across server and client hydration.
130
+ */
131
+ function renderIcon(state, props, config = defaultIconAppearance, instanceId = "icon") {
132
+ const { source, name } = selectIconSource(props);
133
+ const set = sourceSet(source);
134
+ const sourceName = typeof source === "string" ? source : void 0;
135
+ const size = props.size ?? resolveSetOption(config.defaultSize, set) ?? defaultIconAppearance.defaultSize;
136
+ const sizeValue = typeof size === "number" ? size : resolveSizeValue(config.sizeValues, size, set) ?? ownValue(defaultIconAppearance.sizeValues, size) ?? size;
137
+ const width = props.size === void 0 ? props.width ?? sizeValue : sizeValue;
138
+ const height = props.size === void 0 ? props.height ?? sizeValue : sizeValue;
139
+ const rawData = state.data;
140
+ const tuples = isElementData(rawData);
141
+ let data = tuples ? elementDataToIcon([...rawData.filter(([, attrs]) => attrs.opacity !== void 0), ...rawData.filter(([, attrs]) => attrs.opacity === void 0)], props.fill ?? state.fill ?? "none") : rawData;
142
+ if (data) data = withIconViewBox(data, sourceName);
143
+ if (state.href) {
144
+ const [left, top, w, h] = (state.viewBox ?? getIconViewBox(sourceName)).split(" ").map(Number);
145
+ data = {
146
+ left,
147
+ top,
148
+ width: w,
149
+ height: h,
150
+ body: `<use href="${state.href.replaceAll("&", "&amp;").replaceAll("\"", "&quot;")}" width="${w}" height="${h}"/>`
151
+ };
152
+ }
153
+ const rendered = data ? renderSvgData(data, {
154
+ ...props.rotate === void 0 ? {} : { rotate: props.rotate },
155
+ ...props.hFlip === void 0 ? {} : { hFlip: props.hFlip },
156
+ ...props.vFlip === void 0 ? {} : { vFlip: props.vFlip }
157
+ }) : void 0;
158
+ const viewBox = rendered?.viewBox ?? getIconViewBox(sourceName);
159
+ const [, , boxWidth, boxHeight] = viewBox.split(" ").map(Number);
160
+ const scale = Math.min(numericLength(width) / boxWidth, numericLength(height) / boxHeight);
161
+ const stroke = props.strokeWidth ?? resolveSetOption(config.strokeWidth, set) ?? defaultIconAppearance.strokeWidth;
162
+ const strokeWidth = (props.absoluteStrokeWidth ?? resolveSetOption(config.absoluteStrokeWidth, set) ?? defaultIconAppearance.absoluteStrokeWidth) && scale > 0 ? stroke / scale : stroke * Math.max(boxWidth, boxHeight) / 24;
163
+ return {
164
+ available: !!rendered,
165
+ attributes: {
166
+ xmlns: "http://www.w3.org/2000/svg",
167
+ "xmlns:xlink": "http://www.w3.org/1999/xlink",
168
+ width,
169
+ height,
170
+ viewBox,
171
+ fill: props.fill ?? state.fill ?? (tuples ? "none" : "currentColor"),
172
+ color: props.color ?? "currentColor",
173
+ "stroke-width": strokeWidth,
174
+ "aria-label": props["aria-label"],
175
+ "aria-hidden": props["aria-hidden"] ?? (props["aria-label"] !== void 0 || props.role !== void 0 ? void 0 : true),
176
+ role: props.role,
177
+ "data-icon": typeof source === "string" ? source : name,
178
+ "data-state": state.status
179
+ },
180
+ body: rendered ? replaceSvgIds(configurableStrokeBody(rendered.body), "icon-" + instanceId.replace(/[^a-zA-Z0-9_-]/g, "") + "-") : "",
181
+ style: { "--icones-stroke-width": strokeWidth }
182
+ };
183
+ }
184
+ /** Serialize only style values; framework adapters still escape the HTML attribute. */
185
+ function iconStyleText(style) {
186
+ return Object.entries(style).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}:${value}`).join(";");
187
+ }
188
+ //#endregion
189
+ export { createIconSourceValidator, defaultIconAppearance, iconOptionKeys, iconStyleText, mergeIconAppearance, mergeIconDefaultSize, mergeIconSizeValues, renderIcon, selectIconSource };
@@ -0,0 +1,25 @@
1
+ import { t as isElementData } from "./elements-DLSCanEZ.js";
2
+ import { i as IconSet, o as isIconData, s as isIconSet } from "./icon-data-HGvJGwoB.js";
3
+ import { Data, IconLoadState, IconLoader } from "./types.js";
4
+ import { n as resolveIconSetData, t as parseIconName } from "./sources-CXcG9DjB.js";
5
+ import { IconStore } from "./store.js";
6
+ //#region src/runtime/registry.d.ts
7
+ /** Global source registry used by default imports and explicit registrations. */
8
+ declare const registeredSources: Record<string, Data | IconSet>;
9
+ declare const registryStore: IconStore;
10
+ /** Resolve bare icon names to provider-qualified names when registered with a set prefix. */
11
+ declare function registeredName(name: string): string;
12
+ /** Register a single icon payload into the shared registry cache. */
13
+ declare function addIconData(name: string, data: Data): void;
14
+ /** Register a full icon set and invalidate matching cached keys for hot updates. */
15
+ declare function addIconSet(data: IconSet): void;
16
+ /** Read resolved icon data from registry cache; return null when not found. */
17
+ declare function resolveIconData(name: string): Data | null;
18
+ declare function getIconLoadState(name: string): IconLoadState;
19
+ declare function subscribeIconData(name: string, listener: () => void): () => void;
20
+ /** Load an icon through a custom loader, while preserving registry-level invalidation. */
21
+ declare function loadIconData(name: string, loader: IconLoader): Promise<Data | null>;
22
+ /** Clear one icon or flush all registry sources and set metadata. */
23
+ declare function clearIconData(name?: string): void;
24
+ //#endregion
25
+ export { addIconData, addIconSet, clearIconData, getIconLoadState, isElementData, isIconData, isIconSet, loadIconData, parseIconName, registeredName, registeredSources, registryStore, resolveIconData, resolveIconSetData, subscribeIconData };
@@ -0,0 +1,66 @@
1
+ import { isIconData, isIconSet } from "./icon-data.js";
2
+ import { isElementData } from "./elements.js";
3
+ import { parseIconName, resolveIconSetData } from "./data.js";
4
+ import { iconLoader } from "./runtime.js";
5
+ import { createIconStore } from "./store.js";
6
+ //#region src/runtime/registry.ts
7
+ /** Global source registry used by default imports and explicit registrations. */
8
+ const registeredSources = Object.create(null);
9
+ const registryStore = createIconStore({
10
+ sources: registeredSources,
11
+ api: iconLoader
12
+ });
13
+ const sets = /* @__PURE__ */ new Map();
14
+ /** Resolve bare icon names to provider-qualified names when registered with a set prefix. */
15
+ function registeredName(name) {
16
+ if (Object.hasOwn(registeredSources, name) || name.includes(":")) return name;
17
+ const prefix = [...sets.values()].filter((set) => !set.provider).map((set) => set.prefix).sort((a, b) => b.length - a.length).find((prefix) => name.startsWith(prefix + "-"));
18
+ return prefix ? prefix + ":" + name.slice(prefix.length + 1) : name;
19
+ }
20
+ /** Register a single icon payload into the shared registry cache. */
21
+ function addIconData(name, data) {
22
+ if (!isIconData(data) && !isElementData(data)) throw new TypeError(`Icon ${name} must contain an SVG body.`);
23
+ registeredSources[name] = data;
24
+ registryStore.invalidate(name);
25
+ }
26
+ /** Register a full icon set and invalidate matching cached keys for hot updates. */
27
+ function addIconSet(data) {
28
+ if (!isIconSet(data)) throw new TypeError("Icon set must contain a prefix and icons map.");
29
+ sets.set(`${data.provider ?? ""}:${data.prefix}`, data);
30
+ if (!data.provider) registeredSources[data.prefix] = data;
31
+ else for (const slug of [...Object.keys(data.icons), ...Object.keys(data.aliases ?? {})]) registeredSources[`@${data.provider}:${data.prefix}:${slug}`] = data;
32
+ registryStore.invalidate((name) => {
33
+ const parsed = parseIconName(name);
34
+ const direct = registeredSources[name];
35
+ return parsed?.prefix === data.prefix && parsed.provider === (data.provider ?? "") && (!direct || isIconSet(direct));
36
+ });
37
+ }
38
+ /** Read resolved icon data from registry cache; return null when not found. */
39
+ function resolveIconData(name) {
40
+ return getIconLoadState(name).data ?? null;
41
+ }
42
+ function getIconLoadState(name) {
43
+ return registryStore.getState(registeredName(name));
44
+ }
45
+ function subscribeIconData(name, listener) {
46
+ return registryStore.subscribe(registeredName(name), listener);
47
+ }
48
+ /** Load an icon through a custom loader, while preserving registry-level invalidation. */
49
+ function loadIconData(name, loader) {
50
+ const key = registeredName(name);
51
+ return registryStore.load(key, { loader: (_key, _parsed, request) => loader(name, parseIconName(name), request) });
52
+ }
53
+ /** Clear one icon or flush all registry sources and set metadata. */
54
+ function clearIconData(name) {
55
+ if (name !== void 0) {
56
+ const key = registeredName(name);
57
+ delete registeredSources[name];
58
+ registryStore.invalidate(key);
59
+ return;
60
+ }
61
+ for (const key of Object.keys(registeredSources)) delete registeredSources[key];
62
+ sets.clear();
63
+ registryStore.invalidate();
64
+ }
65
+ //#endregion
66
+ export { addIconData, addIconSet, clearIconData, getIconLoadState, isElementData, isIconData, isIconSet, loadIconData, parseIconName, registeredName, registeredSources, registryStore, resolveIconData, resolveIconSetData, subscribeIconData };
@@ -0,0 +1,68 @@
1
+ //#region src/resources/types.d.ts
2
+ type IconSource = {
3
+ url: string;
4
+ revision?: string;
5
+ importedAt?: string;
6
+ distributionNotice?: string;
7
+ [key: string]: unknown;
8
+ };
9
+ type IconManifest = {
10
+ version: 1;
11
+ prefix: string;
12
+ /** Style → category → flat filenames, relative to data/ and symbols/. */
13
+ variants: Record<string, Record<string, {
14
+ json: string[];
15
+ svg: string[];
16
+ }>>;
17
+ /** Canonical style → upstream display name; never changes icon slugs or sorting. */
18
+ variantAliases?: Partial<Record<"outline" | "solid", string>>;
19
+ /** Upstream provenance, keyed by the original source prefix. */
20
+ sources?: Record<string, IconSource>;
21
+ /** Optional legacy namespace rules; no per-icon path/alias table. */
22
+ aliases?: Record<string, {
23
+ suffix: string;
24
+ }>;
25
+ };
26
+ type ManifestEntry = {
27
+ prefix: string;
28
+ slug: string;
29
+ variant: string;
30
+ variantAlias?: string;
31
+ category: string;
32
+ };
33
+ /** Shared resource metadata, independent of HTTP envelopes and gallery routes. */
34
+ type CatalogIcon = {
35
+ name: string;
36
+ prefix: string;
37
+ category: string;
38
+ variant?: string;
39
+ /** Upstream style name for display only; queries use variant. */
40
+ variantAlias?: string;
41
+ };
42
+ type CatalogFacet = {
43
+ id: string;
44
+ count: number;
45
+ alias?: string;
46
+ };
47
+ type CatalogQuery = {
48
+ set?: string;
49
+ category?: string;
50
+ variant?: string;
51
+ q?: string;
52
+ offset?: number;
53
+ limit?: number;
54
+ /** Optional exact-name suffix filters, not renderer variants. */
55
+ suffix?: string;
56
+ excludeSuffix?: string;
57
+ };
58
+ type CatalogResult = {
59
+ icons: CatalogIcon[];
60
+ sets: CatalogFacet[];
61
+ categories: CatalogFacet[];
62
+ variants?: CatalogFacet[];
63
+ total: number;
64
+ offset: number;
65
+ nextOffset: number | null;
66
+ };
67
+ //#endregion
68
+ export { CatalogFacet, CatalogIcon, CatalogQuery, CatalogResult, IconManifest, IconSource, ManifestEntry };
File without changes
@@ -0,0 +1,7 @@
1
+ import { IconLoader, RuntimeIcon } from "./types.js";
2
+ //#region src/runtime/compiled.d.ts
3
+ declare const iconLoader: IconLoader;
4
+ declare function registerStatic(name: string, icon: RuntimeIcon): void;
5
+ declare function resolve(name: string): RuntimeIcon | undefined;
6
+ //#endregion
7
+ export { iconLoader, registerStatic, resolve };