@tenphi/tasty 0.0.0-snapshot.d2076da → 0.0.0-snapshot.d2dcdeb

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/dist/debug.js CHANGED
@@ -26,7 +26,7 @@ function findDomTastyClasses(root = document) {
26
26
  (root.querySelectorAll?.("[class]") || []).forEach((el) => {
27
27
  const attr = el.getAttribute("class");
28
28
  if (attr) {
29
- for (const cls of attr.split(/\s+/)) if (/^t\d+$/.test(cls)) classes.add(cls);
29
+ for (const cls of attr.split(/\s+/)) if (/^t[a-z0-9]+$/.test(cls)) classes.add(cls);
30
30
  }
31
31
  });
32
32
  return sortTastyClasses(classes);
@@ -222,7 +222,7 @@ const tastyDebug = {
222
222
  css(target, opts) {
223
223
  const { root = document, prettify = true, raw = false, source = false } = opts || {};
224
224
  let css = "";
225
- if (source && typeof target === "string" && /^t\d+$/.test(target)) {
225
+ if (source && typeof target === "string" && /^t[a-z0-9]+$/.test(target)) {
226
226
  const src = getSourceCssForClasses([target], root);
227
227
  if (src) css = src;
228
228
  else {
@@ -247,7 +247,7 @@ const tastyDebug = {
247
247
  const unused = getUnusedClasses(root);
248
248
  css = injector.instance.getCssTextForClasses(unused, { root });
249
249
  } else if (target === "page") css = getPageCSS(root);
250
- else if (/^t\d+$/.test(target)) css = injector.instance.getCssTextForClasses([target], { root });
250
+ else if (/^t[a-z0-9]+$/.test(target)) css = injector.instance.getCssTextForClasses([target], { root });
251
251
  else {
252
252
  const el = root.querySelector?.(target);
253
253
  if (el) css = getCssTextForNode(el, { root });
@@ -279,7 +279,7 @@ const tastyDebug = {
279
279
  if (!raw) console.warn("tastyDebug.inspect: element not found");
280
280
  return empty;
281
281
  }
282
- const tastyClasses = (element.getAttribute("class") || "").split(/\s+/).filter((cls) => /^t\d+$/.test(cls));
282
+ const tastyClasses = (element.getAttribute("class") || "").split(/\s+/).filter((cls) => /^t[a-z0-9]+$/.test(cls));
283
283
  const chunks = tastyClasses.map((className) => ({
284
284
  className,
285
285
  chunkName: getChunkForClass(className, root)
package/dist/debug.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"debug.js","names":[],"sources":["../src/debug.ts"],"sourcesContent":["/* eslint-disable no-console */\nimport { CHUNK_NAMES } from './chunks/definitions';\nimport { getCssTextForNode, injector } from './injector';\nimport type { CacheMetrics, RootRegistry } from './injector/types';\nimport { isDevEnv } from './utils/is-dev-env';\n\ndeclare global {\n interface Window {\n tastyDebug?: typeof tastyDebug;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype CSSTarget =\n | 'all'\n | 'global'\n | 'active'\n | 'unused'\n | 'page'\n | string\n | string[]\n | Element;\n\ninterface DebugOptions {\n root?: Document | ShadowRoot;\n /** Suppress console logging and return data only (default: false) */\n raw?: boolean;\n}\n\ninterface CssOptions extends DebugOptions {\n prettify?: boolean;\n /** Read from stored source CSS (dev-mode only) instead of live CSSOM */\n source?: boolean;\n}\n\ninterface ChunkInfo {\n className: string;\n chunkName: string | null;\n}\n\ninterface InspectResult {\n element?: Element | null;\n classes: string[];\n chunks: ChunkInfo[];\n css: string;\n size: number;\n rules: number;\n}\n\ninterface CacheStatus {\n classes: {\n active: string[];\n unused: string[];\n all: string[];\n };\n metrics: CacheMetrics | null;\n}\n\ninterface ChunkBreakdown {\n byChunk: Record<\n string,\n { classes: string[]; cssSize: number; ruleCount: number }\n >;\n totalChunkTypes: number;\n totalClasses: number;\n}\n\ninterface Summary {\n activeClasses: string[];\n unusedClasses: string[];\n totalStyledClasses: string[];\n\n activeCSSSize: number;\n unusedCSSSize: number;\n globalCSSSize: number;\n rawCSSSize: number;\n keyframesCSSSize: number;\n propertyCSSSize: number;\n totalCSSSize: number;\n\n activeRuleCount: number;\n unusedRuleCount: number;\n globalRuleCount: number;\n rawRuleCount: number;\n keyframesRuleCount: number;\n propertyRuleCount: number;\n totalRuleCount: number;\n\n metrics: CacheMetrics | null;\n definedProperties: string[];\n definedKeyframes: { name: string; refCount: number }[];\n chunkBreakdown: ChunkBreakdown;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction fmtSize(bytes: number): string {\n return bytes > 1024 ? `${(bytes / 1024).toFixed(1)}KB` : `${bytes}B`;\n}\n\nfunction countRules(css: string): number {\n return (css.match(/\\{[^}]*\\}/g) || []).length;\n}\n\nfunction sortTastyClasses(classes: Iterable<string>): string[] {\n return Array.from(classes).sort(\n (a, b) => parseInt(a.slice(1)) - parseInt(b.slice(1)),\n );\n}\n\nfunction getRegistry(\n root: Document | ShadowRoot = document,\n): RootRegistry | undefined {\n return injector.instance._sheetManager?.getRegistry(root);\n}\n\nfunction getUnusedClasses(root: Document | ShadowRoot = document): string[] {\n const registry = getRegistry(root);\n if (!registry) return [];\n const result: string[] = [];\n for (const [cls, rc] of registry.refCounts as Map<string, number>) {\n if (rc === 0) result.push(cls);\n }\n return sortTastyClasses(result);\n}\n\nfunction findDomTastyClasses(root: Document | ShadowRoot = document): string[] {\n const classes = new Set<string>();\n const elements = (root as Document).querySelectorAll?.('[class]') || [];\n elements.forEach((el) => {\n const attr = el.getAttribute('class');\n if (attr) {\n for (const cls of attr.split(/\\s+/)) {\n if (/^t\\d+$/.test(cls)) classes.add(cls);\n }\n }\n });\n return sortTastyClasses(classes);\n}\n\n// ---------------------------------------------------------------------------\n// prettifyCSS — readable output for nested at-rules & comma selectors\n// ---------------------------------------------------------------------------\n\nfunction prettifyCSS(css: string): string {\n if (!css || !css.trim()) return '';\n\n const out: string[] = [];\n let depth = 0;\n const indent = () => ' '.repeat(depth);\n\n let normalized = css.replace(/\\s+/g, ' ').trim();\n // Ensure braces are surrounded by spaces for splitting\n normalized = normalized.replace(/\\s*\\{\\s*/g, ' { ');\n normalized = normalized.replace(/\\s*\\}\\s*/g, ' } ');\n normalized = normalized.replace(/;\\s*/g, '; ');\n\n const tokens = normalized.split(/\\s+/);\n let buf = '';\n\n for (const t of tokens) {\n if (t === '{') {\n // buf contains the selector / at-rule header\n const header = buf.trim();\n if (header) {\n // Split comma-separated selectors onto their own lines\n // but only if the comma is outside parentheses\n const parts = splitOutsideParens(header, ',');\n if (parts.length > 1) {\n out.push(\n parts\n .map((p, idx) =>\n idx === 0\n ? `${indent()}${p.trim()},`\n : `${indent()}${p.trim()}${idx < parts.length - 1 ? ',' : ''}`,\n )\n .join('\\n') + ' {',\n );\n } else {\n out.push(`${indent()}${header} {`);\n }\n } else {\n out.push(`${indent()}{`);\n }\n depth++;\n buf = '';\n } else if (t === '}') {\n // Flush any trailing declarations\n if (buf.trim()) {\n for (const decl of buf.split(';').filter((s) => s.trim())) {\n out.push(`${indent()}${decl.trim()};`);\n }\n buf = '';\n }\n depth = Math.max(0, depth - 1);\n out.push(`${indent()}}`);\n } else if (t.endsWith(';')) {\n buf += ` ${t}`;\n const full = buf.trim();\n if (full) out.push(`${indent()}${full}`);\n buf = '';\n } else {\n buf += ` ${t}`;\n }\n }\n if (buf.trim()) out.push(buf.trim());\n\n return out\n .filter((l) => l.trim())\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n\n/** Split `str` by `sep` only when not inside parentheses */\nfunction splitOutsideParens(str: string, sep: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let start = 0;\n for (let i = 0; i < str.length; i++) {\n const ch = str[i];\n if (ch === '(') depth++;\n else if (ch === ')') depth--;\n else if (depth === 0 && str.startsWith(sep, i)) {\n parts.push(str.slice(start, i));\n start = i + sep.length;\n }\n }\n parts.push(str.slice(start));\n return parts;\n}\n\n// ---------------------------------------------------------------------------\n// Chunk helpers\n// ---------------------------------------------------------------------------\n\nfunction extractChunkName(cacheKey: string): string | null {\n for (const part of cacheKey.split('\\0')) {\n if (part.startsWith('[states:')) continue;\n if (!part.includes(':') && part.length > 0) return part;\n }\n return null;\n}\n\nfunction getChunkForClass(\n className: string,\n root: Document | ShadowRoot = document,\n): string | null {\n const registry = getRegistry(root);\n if (!registry) return null;\n for (const [key, cn] of registry.cacheKeyToClassName) {\n if (cn === className) return extractChunkName(key);\n }\n return null;\n}\n\nfunction buildChunkBreakdown(\n root: Document | ShadowRoot = document,\n): ChunkBreakdown {\n const registry = getRegistry(root);\n if (!registry) return { byChunk: {}, totalChunkTypes: 0, totalClasses: 0 };\n\n const byChunk: ChunkBreakdown['byChunk'] = {};\n for (const [cacheKey, className] of registry.cacheKeyToClassName) {\n const chunk = extractChunkName(cacheKey) || 'unknown';\n if (!byChunk[chunk])\n byChunk[chunk] = { classes: [], cssSize: 0, ruleCount: 0 };\n byChunk[chunk].classes.push(className);\n const css = injector.instance.getCssTextForClasses([className], { root });\n byChunk[chunk].cssSize += css.length;\n byChunk[chunk].ruleCount += countRules(css);\n }\n\n for (const entry of Object.values(byChunk)) {\n entry.classes = sortTastyClasses(entry.classes);\n }\n\n const totalClasses = Object.values(byChunk).reduce(\n (s, e) => s + e.classes.length,\n 0,\n );\n return {\n byChunk,\n totalChunkTypes: Object.keys(byChunk).length,\n totalClasses,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Global-type CSS helper (internal only)\n// ---------------------------------------------------------------------------\n\nfunction getGlobalTypeCSS(\n type: 'global' | 'raw' | 'keyframes' | 'property',\n root: Document | ShadowRoot = document,\n): { css: string; ruleCount: number; size: number } {\n const registry = getRegistry(root);\n if (!registry) return { css: '', ruleCount: 0, size: 0 };\n\n const chunks: string[] = [];\n let rc = 0;\n\n if (type === 'keyframes') {\n for (const [, entry] of registry.keyframesCache) {\n const info = entry.info;\n const sheet = registry.sheets[info.sheetIndex];\n const ss = sheet?.sheet?.sheet;\n if (ss && info.ruleIndex < ss.cssRules.length) {\n const rule = ss.cssRules[info.ruleIndex];\n if (rule) {\n chunks.push(rule.cssText);\n rc++;\n }\n } else if (info.cssText) {\n chunks.push(info.cssText);\n rc++;\n }\n }\n } else {\n const prefix =\n type === 'global' ? 'global:' : type === 'raw' ? 'raw:' : 'property:';\n for (const [key, ri] of registry.globalRules) {\n if (!key.startsWith(prefix)) continue;\n const sheet = registry.sheets[ri.sheetIndex];\n const ss = sheet?.sheet?.sheet;\n if (ss) {\n const start = Math.max(0, ri.ruleIndex);\n const end = Math.min(\n ss.cssRules.length - 1,\n (ri.endRuleIndex as number) ?? ri.ruleIndex,\n );\n if (start >= 0 && end >= start && start < ss.cssRules.length) {\n for (let i = start; i <= end; i++) {\n const rule = ss.cssRules[i];\n if (rule) {\n chunks.push(rule.cssText);\n rc++;\n }\n }\n }\n } else if (ri.cssText?.length) {\n chunks.push(...ri.cssText);\n rc += ri.cssText.length;\n }\n }\n }\n\n const raw = chunks.join('\\n');\n return { css: prettifyCSS(raw), ruleCount: rc, size: raw.length };\n}\n\n// ---------------------------------------------------------------------------\n// Source CSS (dev-mode RuleInfo.cssText)\n// ---------------------------------------------------------------------------\n\nfunction getSourceCssForClasses(\n classNames: string[],\n root: Document | ShadowRoot = document,\n): string | null {\n const registry = getRegistry(root);\n if (!registry) return null;\n\n const chunks: string[] = [];\n let found = false;\n for (const cls of classNames) {\n const info = registry.rules.get(cls);\n if (info?.cssText?.length) {\n chunks.push(...info.cssText);\n found = true;\n }\n }\n return found ? chunks.join('\\n') : null;\n}\n\n// ---------------------------------------------------------------------------\n// Definitions helper (internal)\n// ---------------------------------------------------------------------------\n\nfunction getDefs(root: Document | ShadowRoot = document) {\n const registry = getRegistry(root);\n let properties: string[] = [];\n if (registry?.injectedProperties) {\n properties = Array.from(\n (registry.injectedProperties as Map<string, string>).keys(),\n ).sort();\n }\n\n const keyframes: { name: string; refCount: number }[] = [];\n if (registry) {\n for (const entry of registry.keyframesCache.values()) {\n keyframes.push({ name: entry.name, refCount: entry.refCount });\n }\n keyframes.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n return { properties, keyframes };\n}\n\n// ---------------------------------------------------------------------------\n// Chunk display order\n// ---------------------------------------------------------------------------\n\nconst CHUNK_ORDER = [\n CHUNK_NAMES.COMBINED,\n CHUNK_NAMES.APPEARANCE,\n CHUNK_NAMES.FONT,\n CHUNK_NAMES.DIMENSION,\n CHUNK_NAMES.DISPLAY,\n CHUNK_NAMES.LAYOUT,\n CHUNK_NAMES.POSITION,\n CHUNK_NAMES.MISC,\n CHUNK_NAMES.SUBCOMPONENTS,\n];\n\n// ---------------------------------------------------------------------------\n// tastyDebug API\n// ---------------------------------------------------------------------------\n\nexport const tastyDebug = {\n css(target: CSSTarget, opts?: CssOptions): string {\n const {\n root = document,\n prettify = true,\n raw = false,\n source = false,\n } = opts || {};\n let css = '';\n\n if (source && typeof target === 'string' && /^t\\d+$/.test(target)) {\n const src = getSourceCssForClasses([target], root);\n if (src) {\n css = src;\n } else {\n if (!raw) {\n console.warn(\n 'tastyDebug: source CSS not available (requires dev mode or TASTY_DEBUG=true). Falling back to live CSSOM.',\n );\n }\n css = injector.instance.getCssTextForClasses([target], { root });\n }\n } else if (source && Array.isArray(target)) {\n const src = getSourceCssForClasses(target, root);\n if (src) {\n css = src;\n } else {\n if (!raw) {\n console.warn(\n 'tastyDebug: source CSS not available. Falling back to live CSSOM.',\n );\n }\n css = injector.instance.getCssTextForClasses(target, { root });\n }\n } else if (typeof target === 'string') {\n if (target === 'all') {\n css = injector.instance.getCssText({ root });\n } else if (target === 'global') {\n css = getGlobalTypeCSS('global', root).css;\n return css; // already prettified\n } else if (target === 'active') {\n const active = findDomTastyClasses(root);\n css = injector.instance.getCssTextForClasses(active, { root });\n } else if (target === 'unused') {\n const unused = getUnusedClasses(root);\n css = injector.instance.getCssTextForClasses(unused, { root });\n } else if (target === 'page') {\n css = getPageCSS(root);\n } else if (/^t\\d+$/.test(target)) {\n css = injector.instance.getCssTextForClasses([target], { root });\n } else {\n const el = (root as Document).querySelector?.(target);\n if (el) css = getCssTextForNode(el, { root });\n }\n } else if (Array.isArray(target)) {\n css = injector.instance.getCssTextForClasses(target, { root });\n } else if (target instanceof Element) {\n css = getCssTextForNode(target, { root });\n }\n\n const result = prettify ? prettifyCSS(css) : css;\n\n if (!raw) {\n const label = Array.isArray(target) ? `[${target.join(', ')}]` : target;\n const rc = countRules(css);\n console.group(`CSS for ${label} (${rc} rules, ${fmtSize(css.length)})`);\n console.log(result || '(empty)');\n console.groupEnd();\n }\n\n return result;\n },\n\n inspect(target: string | Element, opts?: DebugOptions): InspectResult {\n const { root = document, raw = false } = opts || {};\n const element =\n typeof target === 'string'\n ? (root as Document).querySelector?.(target)\n : target;\n\n if (!element) {\n const empty: InspectResult = {\n element: null,\n classes: [],\n chunks: [],\n css: '',\n size: 0,\n rules: 0,\n };\n if (!raw) console.warn('tastyDebug.inspect: element not found');\n return empty;\n }\n\n const classList = element.getAttribute('class') || '';\n const tastyClasses = classList\n .split(/\\s+/)\n .filter((cls) => /^t\\d+$/.test(cls));\n\n const chunks: ChunkInfo[] = tastyClasses.map((className) => ({\n className,\n chunkName: getChunkForClass(className, root),\n }));\n\n const css = getCssTextForNode(element, { root });\n const rules = countRules(css);\n\n const result: InspectResult = {\n element,\n classes: tastyClasses,\n chunks,\n css: prettifyCSS(css),\n size: css.length,\n rules,\n };\n\n if (!raw) {\n const tag = element.tagName.toLowerCase();\n const id = element.id ? `#${element.id}` : '';\n console.group(\n `inspect ${tag}${id} — ${tastyClasses.length} classes, ${rules} rules, ${fmtSize(css.length)}`,\n );\n if (chunks.length) {\n console.log(\n 'Chunks:',\n chunks.map((c) => `${c.className}→${c.chunkName || '?'}`).join(', '),\n );\n }\n console.groupCollapsed('CSS');\n console.log(result.css || '(empty)');\n console.groupEnd();\n console.groupEnd();\n }\n\n return result;\n },\n\n summary(opts?: DebugOptions): Summary {\n const { root = document, raw = false } = opts || {};\n\n const activeClasses = findDomTastyClasses(root);\n const unusedClasses = getUnusedClasses(root);\n const totalStyledClasses = [...activeClasses, ...unusedClasses];\n\n const activeCSS = injector.instance.getCssTextForClasses(activeClasses, {\n root,\n });\n const unusedCSS = injector.instance.getCssTextForClasses(unusedClasses, {\n root,\n });\n const allCSS = injector.instance.getCssText({ root });\n\n const activeRuleCount = countRules(activeCSS);\n const unusedRuleCount = countRules(unusedCSS);\n\n const globalData = getGlobalTypeCSS('global', root);\n const rawData = getGlobalTypeCSS('raw', root);\n const kfData = getGlobalTypeCSS('keyframes', root);\n const propData = getGlobalTypeCSS('property', root);\n\n const totalRuleCount =\n activeRuleCount +\n unusedRuleCount +\n globalData.ruleCount +\n rawData.ruleCount +\n kfData.ruleCount +\n propData.ruleCount;\n\n const metrics = injector.instance.getMetrics({ root });\n const defs = getDefs(root);\n const chunkBreakdown = buildChunkBreakdown(root);\n\n const summary: Summary = {\n activeClasses,\n unusedClasses,\n totalStyledClasses,\n activeCSSSize: activeCSS.length,\n unusedCSSSize: unusedCSS.length,\n globalCSSSize: globalData.size,\n rawCSSSize: rawData.size,\n keyframesCSSSize: kfData.size,\n propertyCSSSize: propData.size,\n totalCSSSize: allCSS.length,\n activeRuleCount,\n unusedRuleCount,\n globalRuleCount: globalData.ruleCount,\n rawRuleCount: rawData.ruleCount,\n keyframesRuleCount: kfData.ruleCount,\n propertyRuleCount: propData.ruleCount,\n totalRuleCount,\n metrics,\n definedProperties: defs.properties,\n definedKeyframes: defs.keyframes,\n chunkBreakdown,\n };\n\n if (!raw) {\n console.group('Tasty Summary');\n console.log(\n `Active: ${activeClasses.length} classes, ${activeRuleCount} rules, ${fmtSize(activeCSS.length)}`,\n );\n console.log(\n `Unused: ${unusedClasses.length} classes, ${unusedRuleCount} rules, ${fmtSize(unusedCSS.length)}`,\n );\n console.log(\n `Global: ${globalData.ruleCount} rules, ${fmtSize(globalData.size)}`,\n );\n if (rawData.ruleCount)\n console.log(\n `Raw: ${rawData.ruleCount} rules, ${fmtSize(rawData.size)}`,\n );\n if (kfData.ruleCount)\n console.log(\n `Keyframes: ${kfData.ruleCount} rules, ${fmtSize(kfData.size)}`,\n );\n if (propData.ruleCount)\n console.log(\n `@property: ${propData.ruleCount} rules, ${fmtSize(propData.size)}`,\n );\n console.log(\n `Total: ${totalStyledClasses.length} classes, ${totalRuleCount} rules, ${fmtSize(allCSS.length)}`,\n );\n\n if (metrics) {\n const total = metrics.hits + metrics.misses;\n const rate = total > 0 ? ((metrics.hits / total) * 100).toFixed(1) : 0;\n console.log(`Cache: ${rate}% hit rate (${total} lookups)`);\n }\n\n if (chunkBreakdown.totalChunkTypes > 0) {\n console.groupCollapsed(\n `Chunks (${chunkBreakdown.totalChunkTypes} types, ${chunkBreakdown.totalClasses} classes)`,\n );\n for (const name of CHUNK_ORDER) {\n const d = chunkBreakdown.byChunk[name];\n if (d)\n console.log(\n ` ${name}: ${d.classes.length} cls, ${d.ruleCount} rules, ${fmtSize(d.cssSize)}`,\n );\n }\n for (const [name, d] of Object.entries(chunkBreakdown.byChunk)) {\n if (!CHUNK_ORDER.includes(name as (typeof CHUNK_ORDER)[number]))\n console.log(\n ` ${name}: ${d.classes.length} cls, ${d.ruleCount} rules, ${fmtSize(d.cssSize)}`,\n );\n }\n console.groupEnd();\n }\n\n if (defs.properties.length || defs.keyframes.length) {\n console.log(\n `Defs: ${defs.properties.length} @property, ${defs.keyframes.length} @keyframes`,\n );\n }\n\n console.groupEnd();\n }\n\n return summary;\n },\n\n chunks(opts?: DebugOptions): ChunkBreakdown {\n const { root = document, raw = false } = opts || {};\n const breakdown = buildChunkBreakdown(root);\n\n if (!raw) {\n console.group(\n `Chunks (${breakdown.totalChunkTypes} types, ${breakdown.totalClasses} classes)`,\n );\n for (const name of CHUNK_ORDER) {\n const d = breakdown.byChunk[name];\n if (d)\n console.log(\n ` ${name}: ${d.classes.length} cls, ${d.ruleCount} rules, ${fmtSize(d.cssSize)}`,\n );\n }\n for (const [name, d] of Object.entries(breakdown.byChunk)) {\n if (!CHUNK_ORDER.includes(name as (typeof CHUNK_ORDER)[number]))\n console.log(\n ` ${name}: ${d.classes.length} cls, ${d.ruleCount} rules, ${fmtSize(d.cssSize)}`,\n );\n }\n console.groupEnd();\n }\n\n return breakdown;\n },\n\n cache(opts?: DebugOptions): CacheStatus {\n const { root = document, raw = false } = opts || {};\n const active = findDomTastyClasses(root);\n const unused = getUnusedClasses(root);\n const metrics = injector.instance.getMetrics({ root });\n\n const status: CacheStatus = {\n classes: { active, unused, all: [...active, ...unused] },\n metrics,\n };\n\n if (!raw) {\n console.group('Cache');\n console.log(`Active: ${active.length}, Unused: ${unused.length}`);\n if (metrics) {\n const total = metrics.hits + metrics.misses;\n const rate = total > 0 ? ((metrics.hits / total) * 100).toFixed(1) : 0;\n console.log(\n `Hits: ${metrics.hits}, Misses: ${metrics.misses}, Rate: ${rate}%`,\n );\n }\n console.groupEnd();\n }\n\n return status;\n },\n\n cleanup(opts?: { root?: Document | ShadowRoot }): void {\n injector.instance.cleanup(opts?.root);\n },\n\n help(): void {\n console.log(`tastyDebug API:\n .summary() — overview (classes, rules, sizes)\n .css(\"active\") — CSS for classes in DOM\n .css(\"t42\") — CSS for a specific class\n .css(\"t42\",{source:1})— original CSS before browser parsing (dev only)\n .css(\".selector\") — CSS for a DOM element\n .inspect(\".selector\") — element details (classes, chunks, rules)\n .chunks() — style chunk breakdown\n .cache() — cache status and metrics\n .cleanup() — force unused style cleanup\nOptions: { raw: true } suppresses logging, { root: shadowRoot } targets Shadow DOM`);\n },\n\n install(): void {\n if (typeof window !== 'undefined' && window.tastyDebug !== tastyDebug) {\n window.tastyDebug = tastyDebug;\n console.log('tastyDebug installed. Run tastyDebug.help() for commands.');\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// Page CSS (minimal, kept internal)\n// ---------------------------------------------------------------------------\n\nfunction getPageCSS(root: Document | ShadowRoot = document): string {\n const chunks: string[] = [];\n try {\n if ('styleSheets' in root) {\n for (const sheet of Array.from((root as Document).styleSheets)) {\n try {\n if (sheet.cssRules)\n chunks.push(\n Array.from(sheet.cssRules)\n .map((r) => r.cssText)\n .join('\\n'),\n );\n } catch {\n /* cross-origin */\n }\n }\n }\n } catch {\n /* ignore */\n }\n return chunks.join('\\n');\n}\n\n// ---------------------------------------------------------------------------\n// Auto-install in development\n// ---------------------------------------------------------------------------\n\nif (typeof window !== 'undefined' && isDevEnv()) {\n tastyDebug.install();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"debug.js","names":[],"sources":["../src/debug.ts"],"sourcesContent":["/* eslint-disable no-console */\nimport { CHUNK_NAMES } from './chunks/definitions';\nimport { getCssTextForNode, injector } from './injector';\nimport type { CacheMetrics, RootRegistry } from './injector/types';\nimport { isDevEnv } from './utils/is-dev-env';\n\ndeclare global {\n interface Window {\n tastyDebug?: typeof tastyDebug;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ntype CSSTarget =\n | 'all'\n | 'global'\n | 'active'\n | 'unused'\n | 'page'\n | string\n | string[]\n | Element;\n\ninterface DebugOptions {\n root?: Document | ShadowRoot;\n /** Suppress console logging and return data only (default: false) */\n raw?: boolean;\n}\n\ninterface CssOptions extends DebugOptions {\n prettify?: boolean;\n /** Read from stored source CSS (dev-mode only) instead of live CSSOM */\n source?: boolean;\n}\n\ninterface ChunkInfo {\n className: string;\n chunkName: string | null;\n}\n\ninterface InspectResult {\n element?: Element | null;\n classes: string[];\n chunks: ChunkInfo[];\n css: string;\n size: number;\n rules: number;\n}\n\ninterface CacheStatus {\n classes: {\n active: string[];\n unused: string[];\n all: string[];\n };\n metrics: CacheMetrics | null;\n}\n\ninterface ChunkBreakdown {\n byChunk: Record<\n string,\n { classes: string[]; cssSize: number; ruleCount: number }\n >;\n totalChunkTypes: number;\n totalClasses: number;\n}\n\ninterface Summary {\n activeClasses: string[];\n unusedClasses: string[];\n totalStyledClasses: string[];\n\n activeCSSSize: number;\n unusedCSSSize: number;\n globalCSSSize: number;\n rawCSSSize: number;\n keyframesCSSSize: number;\n propertyCSSSize: number;\n totalCSSSize: number;\n\n activeRuleCount: number;\n unusedRuleCount: number;\n globalRuleCount: number;\n rawRuleCount: number;\n keyframesRuleCount: number;\n propertyRuleCount: number;\n totalRuleCount: number;\n\n metrics: CacheMetrics | null;\n definedProperties: string[];\n definedKeyframes: { name: string; refCount: number }[];\n chunkBreakdown: ChunkBreakdown;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction fmtSize(bytes: number): string {\n return bytes > 1024 ? `${(bytes / 1024).toFixed(1)}KB` : `${bytes}B`;\n}\n\nfunction countRules(css: string): number {\n return (css.match(/\\{[^}]*\\}/g) || []).length;\n}\n\nfunction sortTastyClasses(classes: Iterable<string>): string[] {\n return Array.from(classes).sort(\n (a, b) => parseInt(a.slice(1)) - parseInt(b.slice(1)),\n );\n}\n\nfunction getRegistry(\n root: Document | ShadowRoot = document,\n): RootRegistry | undefined {\n return injector.instance._sheetManager?.getRegistry(root);\n}\n\nfunction getUnusedClasses(root: Document | ShadowRoot = document): string[] {\n const registry = getRegistry(root);\n if (!registry) return [];\n const result: string[] = [];\n for (const [cls, rc] of registry.refCounts as Map<string, number>) {\n if (rc === 0) result.push(cls);\n }\n return sortTastyClasses(result);\n}\n\nfunction findDomTastyClasses(root: Document | ShadowRoot = document): string[] {\n const classes = new Set<string>();\n const elements = (root as Document).querySelectorAll?.('[class]') || [];\n elements.forEach((el) => {\n const attr = el.getAttribute('class');\n if (attr) {\n for (const cls of attr.split(/\\s+/)) {\n if (/^t[a-z0-9]+$/.test(cls)) classes.add(cls);\n }\n }\n });\n return sortTastyClasses(classes);\n}\n\n// ---------------------------------------------------------------------------\n// prettifyCSS — readable output for nested at-rules & comma selectors\n// ---------------------------------------------------------------------------\n\nfunction prettifyCSS(css: string): string {\n if (!css || !css.trim()) return '';\n\n const out: string[] = [];\n let depth = 0;\n const indent = () => ' '.repeat(depth);\n\n let normalized = css.replace(/\\s+/g, ' ').trim();\n // Ensure braces are surrounded by spaces for splitting\n normalized = normalized.replace(/\\s*\\{\\s*/g, ' { ');\n normalized = normalized.replace(/\\s*\\}\\s*/g, ' } ');\n normalized = normalized.replace(/;\\s*/g, '; ');\n\n const tokens = normalized.split(/\\s+/);\n let buf = '';\n\n for (const t of tokens) {\n if (t === '{') {\n // buf contains the selector / at-rule header\n const header = buf.trim();\n if (header) {\n // Split comma-separated selectors onto their own lines\n // but only if the comma is outside parentheses\n const parts = splitOutsideParens(header, ',');\n if (parts.length > 1) {\n out.push(\n parts\n .map((p, idx) =>\n idx === 0\n ? `${indent()}${p.trim()},`\n : `${indent()}${p.trim()}${idx < parts.length - 1 ? ',' : ''}`,\n )\n .join('\\n') + ' {',\n );\n } else {\n out.push(`${indent()}${header} {`);\n }\n } else {\n out.push(`${indent()}{`);\n }\n depth++;\n buf = '';\n } else if (t === '}') {\n // Flush any trailing declarations\n if (buf.trim()) {\n for (const decl of buf.split(';').filter((s) => s.trim())) {\n out.push(`${indent()}${decl.trim()};`);\n }\n buf = '';\n }\n depth = Math.max(0, depth - 1);\n out.push(`${indent()}}`);\n } else if (t.endsWith(';')) {\n buf += ` ${t}`;\n const full = buf.trim();\n if (full) out.push(`${indent()}${full}`);\n buf = '';\n } else {\n buf += ` ${t}`;\n }\n }\n if (buf.trim()) out.push(buf.trim());\n\n return out\n .filter((l) => l.trim())\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n\n/** Split `str` by `sep` only when not inside parentheses */\nfunction splitOutsideParens(str: string, sep: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let start = 0;\n for (let i = 0; i < str.length; i++) {\n const ch = str[i];\n if (ch === '(') depth++;\n else if (ch === ')') depth--;\n else if (depth === 0 && str.startsWith(sep, i)) {\n parts.push(str.slice(start, i));\n start = i + sep.length;\n }\n }\n parts.push(str.slice(start));\n return parts;\n}\n\n// ---------------------------------------------------------------------------\n// Chunk helpers\n// ---------------------------------------------------------------------------\n\nfunction extractChunkName(cacheKey: string): string | null {\n for (const part of cacheKey.split('\\0')) {\n if (part.startsWith('[states:')) continue;\n if (!part.includes(':') && part.length > 0) return part;\n }\n return null;\n}\n\nfunction getChunkForClass(\n className: string,\n root: Document | ShadowRoot = document,\n): string | null {\n const registry = getRegistry(root);\n if (!registry) return null;\n for (const [key, cn] of registry.cacheKeyToClassName) {\n if (cn === className) return extractChunkName(key);\n }\n return null;\n}\n\nfunction buildChunkBreakdown(\n root: Document | ShadowRoot = document,\n): ChunkBreakdown {\n const registry = getRegistry(root);\n if (!registry) return { byChunk: {}, totalChunkTypes: 0, totalClasses: 0 };\n\n const byChunk: ChunkBreakdown['byChunk'] = {};\n for (const [cacheKey, className] of registry.cacheKeyToClassName) {\n const chunk = extractChunkName(cacheKey) || 'unknown';\n if (!byChunk[chunk])\n byChunk[chunk] = { classes: [], cssSize: 0, ruleCount: 0 };\n byChunk[chunk].classes.push(className);\n const css = injector.instance.getCssTextForClasses([className], { root });\n byChunk[chunk].cssSize += css.length;\n byChunk[chunk].ruleCount += countRules(css);\n }\n\n for (const entry of Object.values(byChunk)) {\n entry.classes = sortTastyClasses(entry.classes);\n }\n\n const totalClasses = Object.values(byChunk).reduce(\n (s, e) => s + e.classes.length,\n 0,\n );\n return {\n byChunk,\n totalChunkTypes: Object.keys(byChunk).length,\n totalClasses,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Global-type CSS helper (internal only)\n// ---------------------------------------------------------------------------\n\nfunction getGlobalTypeCSS(\n type: 'global' | 'raw' | 'keyframes' | 'property',\n root: Document | ShadowRoot = document,\n): { css: string; ruleCount: number; size: number } {\n const registry = getRegistry(root);\n if (!registry) return { css: '', ruleCount: 0, size: 0 };\n\n const chunks: string[] = [];\n let rc = 0;\n\n if (type === 'keyframes') {\n for (const [, entry] of registry.keyframesCache) {\n const info = entry.info;\n const sheet = registry.sheets[info.sheetIndex];\n const ss = sheet?.sheet?.sheet;\n if (ss && info.ruleIndex < ss.cssRules.length) {\n const rule = ss.cssRules[info.ruleIndex];\n if (rule) {\n chunks.push(rule.cssText);\n rc++;\n }\n } else if (info.cssText) {\n chunks.push(info.cssText);\n rc++;\n }\n }\n } else {\n const prefix =\n type === 'global' ? 'global:' : type === 'raw' ? 'raw:' : 'property:';\n for (const [key, ri] of registry.globalRules) {\n if (!key.startsWith(prefix)) continue;\n const sheet = registry.sheets[ri.sheetIndex];\n const ss = sheet?.sheet?.sheet;\n if (ss) {\n const start = Math.max(0, ri.ruleIndex);\n const end = Math.min(\n ss.cssRules.length - 1,\n (ri.endRuleIndex as number) ?? ri.ruleIndex,\n );\n if (start >= 0 && end >= start && start < ss.cssRules.length) {\n for (let i = start; i <= end; i++) {\n const rule = ss.cssRules[i];\n if (rule) {\n chunks.push(rule.cssText);\n rc++;\n }\n }\n }\n } else if (ri.cssText?.length) {\n chunks.push(...ri.cssText);\n rc += ri.cssText.length;\n }\n }\n }\n\n const raw = chunks.join('\\n');\n return { css: prettifyCSS(raw), ruleCount: rc, size: raw.length };\n}\n\n// ---------------------------------------------------------------------------\n// Source CSS (dev-mode RuleInfo.cssText)\n// ---------------------------------------------------------------------------\n\nfunction getSourceCssForClasses(\n classNames: string[],\n root: Document | ShadowRoot = document,\n): string | null {\n const registry = getRegistry(root);\n if (!registry) return null;\n\n const chunks: string[] = [];\n let found = false;\n for (const cls of classNames) {\n const info = registry.rules.get(cls);\n if (info?.cssText?.length) {\n chunks.push(...info.cssText);\n found = true;\n }\n }\n return found ? chunks.join('\\n') : null;\n}\n\n// ---------------------------------------------------------------------------\n// Definitions helper (internal)\n// ---------------------------------------------------------------------------\n\nfunction getDefs(root: Document | ShadowRoot = document) {\n const registry = getRegistry(root);\n let properties: string[] = [];\n if (registry?.injectedProperties) {\n properties = Array.from(\n (registry.injectedProperties as Map<string, string>).keys(),\n ).sort();\n }\n\n const keyframes: { name: string; refCount: number }[] = [];\n if (registry) {\n for (const entry of registry.keyframesCache.values()) {\n keyframes.push({ name: entry.name, refCount: entry.refCount });\n }\n keyframes.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n return { properties, keyframes };\n}\n\n// ---------------------------------------------------------------------------\n// Chunk display order\n// ---------------------------------------------------------------------------\n\nconst CHUNK_ORDER = [\n CHUNK_NAMES.COMBINED,\n CHUNK_NAMES.APPEARANCE,\n CHUNK_NAMES.FONT,\n CHUNK_NAMES.DIMENSION,\n CHUNK_NAMES.DISPLAY,\n CHUNK_NAMES.LAYOUT,\n CHUNK_NAMES.POSITION,\n CHUNK_NAMES.MISC,\n CHUNK_NAMES.SUBCOMPONENTS,\n];\n\n// ---------------------------------------------------------------------------\n// tastyDebug API\n// ---------------------------------------------------------------------------\n\nexport const tastyDebug = {\n css(target: CSSTarget, opts?: CssOptions): string {\n const {\n root = document,\n prettify = true,\n raw = false,\n source = false,\n } = opts || {};\n let css = '';\n\n if (source && typeof target === 'string' && /^t[a-z0-9]+$/.test(target)) {\n const src = getSourceCssForClasses([target], root);\n if (src) {\n css = src;\n } else {\n if (!raw) {\n console.warn(\n 'tastyDebug: source CSS not available (requires dev mode or TASTY_DEBUG=true). Falling back to live CSSOM.',\n );\n }\n css = injector.instance.getCssTextForClasses([target], { root });\n }\n } else if (source && Array.isArray(target)) {\n const src = getSourceCssForClasses(target, root);\n if (src) {\n css = src;\n } else {\n if (!raw) {\n console.warn(\n 'tastyDebug: source CSS not available. Falling back to live CSSOM.',\n );\n }\n css = injector.instance.getCssTextForClasses(target, { root });\n }\n } else if (typeof target === 'string') {\n if (target === 'all') {\n css = injector.instance.getCssText({ root });\n } else if (target === 'global') {\n css = getGlobalTypeCSS('global', root).css;\n return css; // already prettified\n } else if (target === 'active') {\n const active = findDomTastyClasses(root);\n css = injector.instance.getCssTextForClasses(active, { root });\n } else if (target === 'unused') {\n const unused = getUnusedClasses(root);\n css = injector.instance.getCssTextForClasses(unused, { root });\n } else if (target === 'page') {\n css = getPageCSS(root);\n } else if (/^t[a-z0-9]+$/.test(target)) {\n css = injector.instance.getCssTextForClasses([target], { root });\n } else {\n const el = (root as Document).querySelector?.(target);\n if (el) css = getCssTextForNode(el, { root });\n }\n } else if (Array.isArray(target)) {\n css = injector.instance.getCssTextForClasses(target, { root });\n } else if (target instanceof Element) {\n css = getCssTextForNode(target, { root });\n }\n\n const result = prettify ? prettifyCSS(css) : css;\n\n if (!raw) {\n const label = Array.isArray(target) ? `[${target.join(', ')}]` : target;\n const rc = countRules(css);\n console.group(`CSS for ${label} (${rc} rules, ${fmtSize(css.length)})`);\n console.log(result || '(empty)');\n console.groupEnd();\n }\n\n return result;\n },\n\n inspect(target: string | Element, opts?: DebugOptions): InspectResult {\n const { root = document, raw = false } = opts || {};\n const element =\n typeof target === 'string'\n ? (root as Document).querySelector?.(target)\n : target;\n\n if (!element) {\n const empty: InspectResult = {\n element: null,\n classes: [],\n chunks: [],\n css: '',\n size: 0,\n rules: 0,\n };\n if (!raw) console.warn('tastyDebug.inspect: element not found');\n return empty;\n }\n\n const classList = element.getAttribute('class') || '';\n const tastyClasses = classList\n .split(/\\s+/)\n .filter((cls) => /^t[a-z0-9]+$/.test(cls));\n\n const chunks: ChunkInfo[] = tastyClasses.map((className) => ({\n className,\n chunkName: getChunkForClass(className, root),\n }));\n\n const css = getCssTextForNode(element, { root });\n const rules = countRules(css);\n\n const result: InspectResult = {\n element,\n classes: tastyClasses,\n chunks,\n css: prettifyCSS(css),\n size: css.length,\n rules,\n };\n\n if (!raw) {\n const tag = element.tagName.toLowerCase();\n const id = element.id ? `#${element.id}` : '';\n console.group(\n `inspect ${tag}${id} — ${tastyClasses.length} classes, ${rules} rules, ${fmtSize(css.length)}`,\n );\n if (chunks.length) {\n console.log(\n 'Chunks:',\n chunks.map((c) => `${c.className}→${c.chunkName || '?'}`).join(', '),\n );\n }\n console.groupCollapsed('CSS');\n console.log(result.css || '(empty)');\n console.groupEnd();\n console.groupEnd();\n }\n\n return result;\n },\n\n summary(opts?: DebugOptions): Summary {\n const { root = document, raw = false } = opts || {};\n\n const activeClasses = findDomTastyClasses(root);\n const unusedClasses = getUnusedClasses(root);\n const totalStyledClasses = [...activeClasses, ...unusedClasses];\n\n const activeCSS = injector.instance.getCssTextForClasses(activeClasses, {\n root,\n });\n const unusedCSS = injector.instance.getCssTextForClasses(unusedClasses, {\n root,\n });\n const allCSS = injector.instance.getCssText({ root });\n\n const activeRuleCount = countRules(activeCSS);\n const unusedRuleCount = countRules(unusedCSS);\n\n const globalData = getGlobalTypeCSS('global', root);\n const rawData = getGlobalTypeCSS('raw', root);\n const kfData = getGlobalTypeCSS('keyframes', root);\n const propData = getGlobalTypeCSS('property', root);\n\n const totalRuleCount =\n activeRuleCount +\n unusedRuleCount +\n globalData.ruleCount +\n rawData.ruleCount +\n kfData.ruleCount +\n propData.ruleCount;\n\n const metrics = injector.instance.getMetrics({ root });\n const defs = getDefs(root);\n const chunkBreakdown = buildChunkBreakdown(root);\n\n const summary: Summary = {\n activeClasses,\n unusedClasses,\n totalStyledClasses,\n activeCSSSize: activeCSS.length,\n unusedCSSSize: unusedCSS.length,\n globalCSSSize: globalData.size,\n rawCSSSize: rawData.size,\n keyframesCSSSize: kfData.size,\n propertyCSSSize: propData.size,\n totalCSSSize: allCSS.length,\n activeRuleCount,\n unusedRuleCount,\n globalRuleCount: globalData.ruleCount,\n rawRuleCount: rawData.ruleCount,\n keyframesRuleCount: kfData.ruleCount,\n propertyRuleCount: propData.ruleCount,\n totalRuleCount,\n metrics,\n definedProperties: defs.properties,\n definedKeyframes: defs.keyframes,\n chunkBreakdown,\n };\n\n if (!raw) {\n console.group('Tasty Summary');\n console.log(\n `Active: ${activeClasses.length} classes, ${activeRuleCount} rules, ${fmtSize(activeCSS.length)}`,\n );\n console.log(\n `Unused: ${unusedClasses.length} classes, ${unusedRuleCount} rules, ${fmtSize(unusedCSS.length)}`,\n );\n console.log(\n `Global: ${globalData.ruleCount} rules, ${fmtSize(globalData.size)}`,\n );\n if (rawData.ruleCount)\n console.log(\n `Raw: ${rawData.ruleCount} rules, ${fmtSize(rawData.size)}`,\n );\n if (kfData.ruleCount)\n console.log(\n `Keyframes: ${kfData.ruleCount} rules, ${fmtSize(kfData.size)}`,\n );\n if (propData.ruleCount)\n console.log(\n `@property: ${propData.ruleCount} rules, ${fmtSize(propData.size)}`,\n );\n console.log(\n `Total: ${totalStyledClasses.length} classes, ${totalRuleCount} rules, ${fmtSize(allCSS.length)}`,\n );\n\n if (metrics) {\n const total = metrics.hits + metrics.misses;\n const rate = total > 0 ? ((metrics.hits / total) * 100).toFixed(1) : 0;\n console.log(`Cache: ${rate}% hit rate (${total} lookups)`);\n }\n\n if (chunkBreakdown.totalChunkTypes > 0) {\n console.groupCollapsed(\n `Chunks (${chunkBreakdown.totalChunkTypes} types, ${chunkBreakdown.totalClasses} classes)`,\n );\n for (const name of CHUNK_ORDER) {\n const d = chunkBreakdown.byChunk[name];\n if (d)\n console.log(\n ` ${name}: ${d.classes.length} cls, ${d.ruleCount} rules, ${fmtSize(d.cssSize)}`,\n );\n }\n for (const [name, d] of Object.entries(chunkBreakdown.byChunk)) {\n if (!CHUNK_ORDER.includes(name as (typeof CHUNK_ORDER)[number]))\n console.log(\n ` ${name}: ${d.classes.length} cls, ${d.ruleCount} rules, ${fmtSize(d.cssSize)}`,\n );\n }\n console.groupEnd();\n }\n\n if (defs.properties.length || defs.keyframes.length) {\n console.log(\n `Defs: ${defs.properties.length} @property, ${defs.keyframes.length} @keyframes`,\n );\n }\n\n console.groupEnd();\n }\n\n return summary;\n },\n\n chunks(opts?: DebugOptions): ChunkBreakdown {\n const { root = document, raw = false } = opts || {};\n const breakdown = buildChunkBreakdown(root);\n\n if (!raw) {\n console.group(\n `Chunks (${breakdown.totalChunkTypes} types, ${breakdown.totalClasses} classes)`,\n );\n for (const name of CHUNK_ORDER) {\n const d = breakdown.byChunk[name];\n if (d)\n console.log(\n ` ${name}: ${d.classes.length} cls, ${d.ruleCount} rules, ${fmtSize(d.cssSize)}`,\n );\n }\n for (const [name, d] of Object.entries(breakdown.byChunk)) {\n if (!CHUNK_ORDER.includes(name as (typeof CHUNK_ORDER)[number]))\n console.log(\n ` ${name}: ${d.classes.length} cls, ${d.ruleCount} rules, ${fmtSize(d.cssSize)}`,\n );\n }\n console.groupEnd();\n }\n\n return breakdown;\n },\n\n cache(opts?: DebugOptions): CacheStatus {\n const { root = document, raw = false } = opts || {};\n const active = findDomTastyClasses(root);\n const unused = getUnusedClasses(root);\n const metrics = injector.instance.getMetrics({ root });\n\n const status: CacheStatus = {\n classes: { active, unused, all: [...active, ...unused] },\n metrics,\n };\n\n if (!raw) {\n console.group('Cache');\n console.log(`Active: ${active.length}, Unused: ${unused.length}`);\n if (metrics) {\n const total = metrics.hits + metrics.misses;\n const rate = total > 0 ? ((metrics.hits / total) * 100).toFixed(1) : 0;\n console.log(\n `Hits: ${metrics.hits}, Misses: ${metrics.misses}, Rate: ${rate}%`,\n );\n }\n console.groupEnd();\n }\n\n return status;\n },\n\n cleanup(opts?: { root?: Document | ShadowRoot }): void {\n injector.instance.cleanup(opts?.root);\n },\n\n help(): void {\n console.log(`tastyDebug API:\n .summary() — overview (classes, rules, sizes)\n .css(\"active\") — CSS for classes in DOM\n .css(\"t42\") — CSS for a specific class\n .css(\"t42\",{source:1})— original CSS before browser parsing (dev only)\n .css(\".selector\") — CSS for a DOM element\n .inspect(\".selector\") — element details (classes, chunks, rules)\n .chunks() — style chunk breakdown\n .cache() — cache status and metrics\n .cleanup() — force unused style cleanup\nOptions: { raw: true } suppresses logging, { root: shadowRoot } targets Shadow DOM`);\n },\n\n install(): void {\n if (typeof window !== 'undefined' && window.tastyDebug !== tastyDebug) {\n window.tastyDebug = tastyDebug;\n console.log('tastyDebug installed. Run tastyDebug.help() for commands.');\n }\n },\n};\n\n// ---------------------------------------------------------------------------\n// Page CSS (minimal, kept internal)\n// ---------------------------------------------------------------------------\n\nfunction getPageCSS(root: Document | ShadowRoot = document): string {\n const chunks: string[] = [];\n try {\n if ('styleSheets' in root) {\n for (const sheet of Array.from((root as Document).styleSheets)) {\n try {\n if (sheet.cssRules)\n chunks.push(\n Array.from(sheet.cssRules)\n .map((r) => r.cssText)\n .join('\\n'),\n );\n } catch {\n /* cross-origin */\n }\n }\n }\n } catch {\n /* ignore */\n }\n return chunks.join('\\n');\n}\n\n// ---------------------------------------------------------------------------\n// Auto-install in development\n// ---------------------------------------------------------------------------\n\nif (typeof window !== 'undefined' && isDevEnv()) {\n tastyDebug.install();\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
+ import { hashString } from "../utils/hash.js";
1
2
  import { formatCounterStyleRule } from "../counter-style/index.js";
2
3
  import { getGlobalInjector } from "../config.js";
3
- import { hashString } from "../utils/hash.js";
4
4
  import { getStyleTarget, pushRSCCSS } from "../rsc-cache.js";
5
5
  //#region src/hooks/useCounterStyle.ts
6
6
  let clientCounterStyleCounter = 0;
@@ -1,7 +1,7 @@
1
+ import { hashString } from "../utils/hash.js";
1
2
  import { renderStyles } from "../pipeline/index.js";
2
3
  import { getConfig } from "../config.js";
3
4
  import { injectGlobal } from "../injector/index.js";
4
- import { hashString } from "../utils/hash.js";
5
5
  import { getStyleTarget, pushRSCCSS } from "../rsc-cache.js";
6
6
  import { collectAutoInferredProperties, collectAutoInferredPropertiesRSC } from "../ssr/collect-auto-properties.js";
7
7
  import { formatGlobalRules } from "../ssr/format-global-rules.js";
@@ -1,5 +1,5 @@
1
- import { keyframes } from "../injector/index.js";
2
1
  import { hashString } from "../utils/hash.js";
2
+ import { keyframes } from "../injector/index.js";
3
3
  import { getStyleTarget, pushRSCCSS } from "../rsc-cache.js";
4
4
  import { formatKeyframesCSS } from "../ssr/format-keyframes.js";
5
5
  import { depsEqual } from "../utils/deps-equal.js";
@@ -1,5 +1,5 @@
1
- import { injectRawCSS } from "../injector/index.js";
2
1
  import { hashString } from "../utils/hash.js";
2
+ import { injectRawCSS } from "../injector/index.js";
3
3
  import { getStyleTarget, pushRSCCSS } from "../rsc-cache.js";
4
4
  import { depsEqual } from "../utils/deps-equal.js";
5
5
  //#region src/hooks/useRawCSS.ts
@@ -119,7 +119,7 @@ function getCssTextForNode(node, options) {
119
119
  const readClasses = (el) => {
120
120
  const cls = el.getAttribute("class");
121
121
  if (!cls) return;
122
- for (const token of cls.split(/\s+/)) if (/^t\d+$/.test(token)) classSet.add(token);
122
+ for (const token of cls.split(/\s+/)) if (/^t[a-z0-9]+$/.test(token)) classSet.add(token);
123
123
  };
124
124
  if (node.getAttribute) readClasses(node);
125
125
  const elements = node.querySelectorAll ? node.querySelectorAll("[class]") : [];
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/injector/index.ts"],"sourcesContent":["import {\n getConfig,\n getGlobalInjector,\n isTestEnvironment,\n markStylesGenerated,\n} from '../config';\nimport type { StyleResult } from '../pipeline';\n\nimport { StyleInjector } from './injector';\nimport type {\n CounterStyleDescriptors,\n FontFaceDescriptors,\n GCOptions,\n GlobalInjectResult,\n InjectResult,\n KeyframesResult,\n KeyframesSteps,\n StyleInjectorConfig,\n} from './types';\n\n/**\n * Inject styles and return className with dispose function\n */\nexport function inject(\n rules: StyleResult[],\n options?: { root?: Document | ShadowRoot; cacheKey?: string },\n): InjectResult {\n const injector = getGlobalInjector();\n\n markStylesGenerated();\n\n return injector.inject(rules, options);\n}\n\n/**\n * Inject global rules that should not reserve tasty class names\n */\nexport function injectGlobal(\n rules: StyleResult[],\n options?: { root?: Document | ShadowRoot },\n): GlobalInjectResult {\n return getGlobalInjector().injectGlobal(rules, options);\n}\n\n/**\n * Inject raw CSS text directly without parsing\n * This is a low-overhead method for injecting raw CSS that doesn't need tasty processing.\n * The CSS is inserted into a separate style element to avoid conflicts with tasty's chunking.\n *\n * @example\n * ```tsx\n * // Inject raw CSS\n * const { dispose } = injectRawCSS(`\n * body { margin: 0; padding: 0; }\n * .my-class { color: red; }\n * `);\n *\n * // Later, remove the injected CSS\n * dispose();\n * ```\n */\nexport function injectRawCSS(\n css: string,\n options?: { root?: Document | ShadowRoot },\n): { dispose: () => void } {\n return getGlobalInjector().injectRawCSS(css, options);\n}\n\n/**\n * Get raw CSS text for SSR\n */\nexport function getRawCSSText(options?: {\n root?: Document | ShadowRoot;\n}): string {\n return getGlobalInjector().getRawCSSText(options);\n}\n\n/**\n * Inject keyframes and return object with toString() and dispose()\n */\nexport function keyframes(\n steps: KeyframesSteps,\n nameOrOptions?: string | { root?: Document | ShadowRoot; name?: string },\n): KeyframesResult {\n return getGlobalInjector().keyframes(steps, nameOrOptions);\n}\n\nexport interface PropertyOptions {\n /**\n * CSS syntax string for the property (e.g., '<color>', '<length>', '<angle>')\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@property/syntax\n */\n syntax?: string;\n /**\n * Whether the property inherits from parent elements\n * @default true\n */\n inherits?: boolean;\n /**\n * Initial value for the property\n */\n initialValue?: string | number;\n /**\n * Shadow root or document to inject into\n */\n root?: Document | ShadowRoot;\n}\n\n/**\n * Define a CSS @property custom property.\n * This enables advanced features like animating custom properties.\n *\n * Note: @property rules are global and persistent once defined.\n * Re-registering the same property name is a no-op.\n *\n * @param name - The custom property name (must start with --)\n * @param options - Property configuration\n *\n * @example\n * ```ts\n * // Define a color property that can be animated\n * property('--my-color', {\n * syntax: '<color>',\n * initialValue: 'red',\n * });\n *\n * // Define an angle property\n * property('--rotation', {\n * syntax: '<angle>',\n * inherits: false,\n * initialValue: '0deg',\n * });\n * ```\n */\nexport function property(name: string, options?: PropertyOptions): void {\n return getGlobalInjector().property(name, options);\n}\n\n/**\n * Check if a CSS @property has already been defined\n *\n * @param name - The custom property name to check\n * @param options - Options including root\n */\nexport function isPropertyDefined(\n name: string,\n options?: { root?: Document | ShadowRoot },\n): boolean {\n return getGlobalInjector().isPropertyDefined(name, options);\n}\n\n/**\n * Inject a CSS @font-face rule.\n *\n * Permanent and global — no dispose or ref-counting.\n * Deduplicates by content hash (family + descriptors).\n */\nexport function fontFace(\n family: string,\n descriptors: FontFaceDescriptors,\n options?: { root?: Document | ShadowRoot },\n): void {\n return getGlobalInjector().fontFace(family, descriptors, options);\n}\n\n/**\n * Inject a CSS @counter-style rule.\n *\n * Permanent and global — no dispose or ref-counting.\n * Deduplicates by name (first definition wins).\n */\nexport function counterStyle(\n name: string,\n descriptors: CounterStyleDescriptors,\n options?: { root?: Document | ShadowRoot },\n): void {\n return getGlobalInjector().counterStyle(name, descriptors, options);\n}\n\n/**\n * Get CSS text from all sheets (for SSR)\n */\nexport function getCssText(options?: { root?: Document | ShadowRoot }): string {\n return getGlobalInjector().getCssText(options);\n}\n\n/**\n * Collect only CSS used by a rendered subtree (like jest-styled-components).\n * Pass the container returned by render(...).\n */\nexport function getCssTextForNode(\n node: ParentNode | Element | DocumentFragment,\n options?: { root?: Document | ShadowRoot },\n): string {\n // Collect tasty-generated class names (t<number>) from the subtree\n const classSet = new Set<string>();\n\n const readClasses = (el: Element) => {\n const cls = el.getAttribute('class');\n if (!cls) return;\n for (const token of cls.split(/\\s+/)) {\n if (/^t\\d+$/.test(token)) classSet.add(token);\n }\n };\n\n // Include node itself if it's an Element\n if ((node as Element).getAttribute) {\n readClasses(node as Element);\n }\n // Walk descendants\n const elements = (node as ParentNode).querySelectorAll\n ? (node as ParentNode).querySelectorAll('[class]')\n : ([] as unknown as NodeListOf<Element>);\n if (elements) elements.forEach(readClasses);\n\n return getGlobalInjector().getCssTextForClasses(classSet, options);\n}\n\n/**\n * Force cleanup of unused rules\n */\nexport function cleanup(root?: Document | ShadowRoot): void {\n return getGlobalInjector().cleanup(root);\n}\n\n/**\n * Record a render-time usage hit for one or more classNames.\n * Used internally by computeStyles and tasty() to track usage for GC.\n * When the global touch counter reaches `touchInterval`, schedules GC.\n */\nexport function touch(\n className: string,\n options?: { root?: Document | ShadowRoot },\n): void {\n if (!getConfig().gc) return;\n getGlobalInjector().touch(className, options);\n}\n\n/**\n * Synchronous garbage collection of unused styles.\n * Evicts the oldest unused styles when usageMap exceeds capacity.\n * With `{ force: true }`, removes ALL unused styles regardless of capacity.\n *\n * @returns Number of styles evicted.\n */\nexport function gc(options?: GCOptions): number {\n return getGlobalInjector().gc(options);\n}\n\n/**\n * Check if we're currently running in a test environment\n */\nexport function getIsTestEnvironment(): boolean {\n return isTestEnvironment();\n}\n\n/**\n * Get the global injector instance for debugging\n */\nexport const injector = {\n get instance() {\n return getGlobalInjector();\n },\n};\n\n/**\n * Destroy all resources and clean up\n */\nexport function destroy(root?: Document | ShadowRoot): void {\n return getGlobalInjector().destroy(root);\n}\n\n/**\n * Create a new isolated injector instance\n */\nexport function createInjector(\n config: Partial<StyleInjectorConfig> = {},\n): StyleInjector {\n const defaultConfig = getConfig();\n\n const fullConfig: StyleInjectorConfig = {\n ...defaultConfig,\n // Auto-enable forceTextInjection in test environments\n forceTextInjection: config.forceTextInjection ?? isTestEnvironment(),\n ...config,\n };\n\n return new StyleInjector(fullConfig);\n}\n\n// Re-export types\nexport type {\n StyleInjectorConfig,\n InjectResult,\n DisposeFunction,\n RuleInfo,\n SheetInfo,\n RootRegistry,\n StyleRule,\n KeyframesInfo,\n KeyframesResult,\n KeyframesSteps,\n KeyframesCacheEntry,\n CacheMetrics,\n RawCSSResult,\n PropertyDefinition,\n FontFaceDescriptors,\n FontFaceInput,\n CounterStyleDescriptors,\n StyleUsage,\n GCConfig,\n GCOptions,\n} from './types';\n\nexport { StyleInjector } from './injector';\nexport { SheetManager } from './sheet-manager';\n"],"mappings":";;;;;;;AAuBA,SAAgB,OACd,OACA,SACc;CACd,MAAM,WAAW,mBAAmB;AAEpC,sBAAqB;AAErB,QAAO,SAAS,OAAO,OAAO,QAAQ;;;;;AAMxC,SAAgB,aACd,OACA,SACoB;AACpB,QAAO,mBAAmB,CAAC,aAAa,OAAO,QAAQ;;;;;;;;;;;;;;;;;;;AAoBzD,SAAgB,aACd,KACA,SACyB;AACzB,QAAO,mBAAmB,CAAC,aAAa,KAAK,QAAQ;;;;;AAMvD,SAAgB,cAAc,SAEnB;AACT,QAAO,mBAAmB,CAAC,cAAc,QAAQ;;;;;AAMnD,SAAgB,UACd,OACA,eACiB;AACjB,QAAO,mBAAmB,CAAC,UAAU,OAAO,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkD5D,SAAgB,SAAS,MAAc,SAAiC;AACtE,QAAO,mBAAmB,CAAC,SAAS,MAAM,QAAQ;;;;;;;;AASpD,SAAgB,kBACd,MACA,SACS;AACT,QAAO,mBAAmB,CAAC,kBAAkB,MAAM,QAAQ;;;;;;;;AAS7D,SAAgB,SACd,QACA,aACA,SACM;AACN,QAAO,mBAAmB,CAAC,SAAS,QAAQ,aAAa,QAAQ;;;;;;;;AASnE,SAAgB,aACd,MACA,aACA,SACM;AACN,QAAO,mBAAmB,CAAC,aAAa,MAAM,aAAa,QAAQ;;;;;AAMrE,SAAgB,WAAW,SAAoD;AAC7E,QAAO,mBAAmB,CAAC,WAAW,QAAQ;;;;;;AAOhD,SAAgB,kBACd,MACA,SACQ;CAER,MAAM,2BAAW,IAAI,KAAa;CAElC,MAAM,eAAe,OAAgB;EACnC,MAAM,MAAM,GAAG,aAAa,QAAQ;AACpC,MAAI,CAAC,IAAK;AACV,OAAK,MAAM,SAAS,IAAI,MAAM,MAAM,CAClC,KAAI,SAAS,KAAK,MAAM,CAAE,UAAS,IAAI,MAAM;;AAKjD,KAAK,KAAiB,aACpB,aAAY,KAAgB;CAG9B,MAAM,WAAY,KAAoB,mBACjC,KAAoB,iBAAiB,UAAU,GAC/C,EAAE;AACP,KAAI,SAAU,UAAS,QAAQ,YAAY;AAE3C,QAAO,mBAAmB,CAAC,qBAAqB,UAAU,QAAQ;;;;;AAMpE,SAAgB,QAAQ,MAAoC;AAC1D,QAAO,mBAAmB,CAAC,QAAQ,KAAK;;;;;;;AAQ1C,SAAgB,MACd,WACA,SACM;AACN,KAAI,CAAC,WAAW,CAAC,GAAI;AACrB,oBAAmB,CAAC,MAAM,WAAW,QAAQ;;;;;;;;;AAU/C,SAAgB,GAAG,SAA6B;AAC9C,QAAO,mBAAmB,CAAC,GAAG,QAAQ;;;;;AAMxC,SAAgB,uBAAgC;AAC9C,QAAO,mBAAmB;;;;;AAM5B,MAAa,WAAW,EACtB,IAAI,WAAW;AACb,QAAO,mBAAmB;GAE7B;;;;AAKD,SAAgB,QAAQ,MAAoC;AAC1D,QAAO,mBAAmB,CAAC,QAAQ,KAAK;;;;;AAM1C,SAAgB,eACd,SAAuC,EAAE,EAC1B;AAUf,QAAO,IAAI,cAP6B;EACtC,GAHoB,WAAW;EAK/B,oBAAoB,OAAO,sBAAsB,mBAAmB;EACpE,GAAG;EACJ,CAEmC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/injector/index.ts"],"sourcesContent":["import {\n getConfig,\n getGlobalInjector,\n isTestEnvironment,\n markStylesGenerated,\n} from '../config';\nimport type { StyleResult } from '../pipeline';\n\nimport { StyleInjector } from './injector';\nimport type {\n CounterStyleDescriptors,\n FontFaceDescriptors,\n GCOptions,\n GlobalInjectResult,\n InjectResult,\n KeyframesResult,\n KeyframesSteps,\n StyleInjectorConfig,\n} from './types';\n\n/**\n * Inject styles and return className with dispose function\n */\nexport function inject(\n rules: StyleResult[],\n options?: { root?: Document | ShadowRoot; cacheKey?: string },\n): InjectResult {\n const injector = getGlobalInjector();\n\n markStylesGenerated();\n\n return injector.inject(rules, options);\n}\n\n/**\n * Inject global rules that should not reserve tasty class names\n */\nexport function injectGlobal(\n rules: StyleResult[],\n options?: { root?: Document | ShadowRoot },\n): GlobalInjectResult {\n return getGlobalInjector().injectGlobal(rules, options);\n}\n\n/**\n * Inject raw CSS text directly without parsing\n * This is a low-overhead method for injecting raw CSS that doesn't need tasty processing.\n * The CSS is inserted into a separate style element to avoid conflicts with tasty's chunking.\n *\n * @example\n * ```tsx\n * // Inject raw CSS\n * const { dispose } = injectRawCSS(`\n * body { margin: 0; padding: 0; }\n * .my-class { color: red; }\n * `);\n *\n * // Later, remove the injected CSS\n * dispose();\n * ```\n */\nexport function injectRawCSS(\n css: string,\n options?: { root?: Document | ShadowRoot },\n): { dispose: () => void } {\n return getGlobalInjector().injectRawCSS(css, options);\n}\n\n/**\n * Get raw CSS text for SSR\n */\nexport function getRawCSSText(options?: {\n root?: Document | ShadowRoot;\n}): string {\n return getGlobalInjector().getRawCSSText(options);\n}\n\n/**\n * Inject keyframes and return object with toString() and dispose()\n */\nexport function keyframes(\n steps: KeyframesSteps,\n nameOrOptions?: string | { root?: Document | ShadowRoot; name?: string },\n): KeyframesResult {\n return getGlobalInjector().keyframes(steps, nameOrOptions);\n}\n\nexport interface PropertyOptions {\n /**\n * CSS syntax string for the property (e.g., '<color>', '<length>', '<angle>')\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@property/syntax\n */\n syntax?: string;\n /**\n * Whether the property inherits from parent elements\n * @default true\n */\n inherits?: boolean;\n /**\n * Initial value for the property\n */\n initialValue?: string | number;\n /**\n * Shadow root or document to inject into\n */\n root?: Document | ShadowRoot;\n}\n\n/**\n * Define a CSS @property custom property.\n * This enables advanced features like animating custom properties.\n *\n * Note: @property rules are global and persistent once defined.\n * Re-registering the same property name is a no-op.\n *\n * @param name - The custom property name (must start with --)\n * @param options - Property configuration\n *\n * @example\n * ```ts\n * // Define a color property that can be animated\n * property('--my-color', {\n * syntax: '<color>',\n * initialValue: 'red',\n * });\n *\n * // Define an angle property\n * property('--rotation', {\n * syntax: '<angle>',\n * inherits: false,\n * initialValue: '0deg',\n * });\n * ```\n */\nexport function property(name: string, options?: PropertyOptions): void {\n return getGlobalInjector().property(name, options);\n}\n\n/**\n * Check if a CSS @property has already been defined\n *\n * @param name - The custom property name to check\n * @param options - Options including root\n */\nexport function isPropertyDefined(\n name: string,\n options?: { root?: Document | ShadowRoot },\n): boolean {\n return getGlobalInjector().isPropertyDefined(name, options);\n}\n\n/**\n * Inject a CSS @font-face rule.\n *\n * Permanent and global — no dispose or ref-counting.\n * Deduplicates by content hash (family + descriptors).\n */\nexport function fontFace(\n family: string,\n descriptors: FontFaceDescriptors,\n options?: { root?: Document | ShadowRoot },\n): void {\n return getGlobalInjector().fontFace(family, descriptors, options);\n}\n\n/**\n * Inject a CSS @counter-style rule.\n *\n * Permanent and global — no dispose or ref-counting.\n * Deduplicates by name (first definition wins).\n */\nexport function counterStyle(\n name: string,\n descriptors: CounterStyleDescriptors,\n options?: { root?: Document | ShadowRoot },\n): void {\n return getGlobalInjector().counterStyle(name, descriptors, options);\n}\n\n/**\n * Get CSS text from all sheets (for SSR)\n */\nexport function getCssText(options?: { root?: Document | ShadowRoot }): string {\n return getGlobalInjector().getCssText(options);\n}\n\n/**\n * Collect only CSS used by a rendered subtree (like jest-styled-components).\n * Pass the container returned by render(...).\n */\nexport function getCssTextForNode(\n node: ParentNode | Element | DocumentFragment,\n options?: { root?: Document | ShadowRoot },\n): string {\n // Collect tasty-generated class names (t<number>) from the subtree\n const classSet = new Set<string>();\n\n const readClasses = (el: Element) => {\n const cls = el.getAttribute('class');\n if (!cls) return;\n for (const token of cls.split(/\\s+/)) {\n if (/^t[a-z0-9]+$/.test(token)) classSet.add(token);\n }\n };\n\n // Include node itself if it's an Element\n if ((node as Element).getAttribute) {\n readClasses(node as Element);\n }\n // Walk descendants\n const elements = (node as ParentNode).querySelectorAll\n ? (node as ParentNode).querySelectorAll('[class]')\n : ([] as unknown as NodeListOf<Element>);\n if (elements) elements.forEach(readClasses);\n\n return getGlobalInjector().getCssTextForClasses(classSet, options);\n}\n\n/**\n * Force cleanup of unused rules\n */\nexport function cleanup(root?: Document | ShadowRoot): void {\n return getGlobalInjector().cleanup(root);\n}\n\n/**\n * Record a render-time usage hit for one or more classNames.\n * Used internally by computeStyles and tasty() to track usage for GC.\n * When the global touch counter reaches `touchInterval`, schedules GC.\n */\nexport function touch(\n className: string,\n options?: { root?: Document | ShadowRoot },\n): void {\n if (!getConfig().gc) return;\n getGlobalInjector().touch(className, options);\n}\n\n/**\n * Synchronous garbage collection of unused styles.\n * Evicts the oldest unused styles when usageMap exceeds capacity.\n * With `{ force: true }`, removes ALL unused styles regardless of capacity.\n *\n * @returns Number of styles evicted.\n */\nexport function gc(options?: GCOptions): number {\n return getGlobalInjector().gc(options);\n}\n\n/**\n * Check if we're currently running in a test environment\n */\nexport function getIsTestEnvironment(): boolean {\n return isTestEnvironment();\n}\n\n/**\n * Get the global injector instance for debugging\n */\nexport const injector = {\n get instance() {\n return getGlobalInjector();\n },\n};\n\n/**\n * Destroy all resources and clean up\n */\nexport function destroy(root?: Document | ShadowRoot): void {\n return getGlobalInjector().destroy(root);\n}\n\n/**\n * Create a new isolated injector instance\n */\nexport function createInjector(\n config: Partial<StyleInjectorConfig> = {},\n): StyleInjector {\n const defaultConfig = getConfig();\n\n const fullConfig: StyleInjectorConfig = {\n ...defaultConfig,\n // Auto-enable forceTextInjection in test environments\n forceTextInjection: config.forceTextInjection ?? isTestEnvironment(),\n ...config,\n };\n\n return new StyleInjector(fullConfig);\n}\n\n// Re-export types\nexport type {\n StyleInjectorConfig,\n InjectResult,\n DisposeFunction,\n RuleInfo,\n SheetInfo,\n RootRegistry,\n StyleRule,\n KeyframesInfo,\n KeyframesResult,\n KeyframesSteps,\n KeyframesCacheEntry,\n CacheMetrics,\n RawCSSResult,\n PropertyDefinition,\n FontFaceDescriptors,\n FontFaceInput,\n CounterStyleDescriptors,\n StyleUsage,\n GCConfig,\n GCOptions,\n} from './types';\n\nexport { StyleInjector } from './injector';\nexport { SheetManager } from './sheet-manager';\n"],"mappings":";;;;;;;AAuBA,SAAgB,OACd,OACA,SACc;CACd,MAAM,WAAW,mBAAmB;AAEpC,sBAAqB;AAErB,QAAO,SAAS,OAAO,OAAO,QAAQ;;;;;AAMxC,SAAgB,aACd,OACA,SACoB;AACpB,QAAO,mBAAmB,CAAC,aAAa,OAAO,QAAQ;;;;;;;;;;;;;;;;;;;AAoBzD,SAAgB,aACd,KACA,SACyB;AACzB,QAAO,mBAAmB,CAAC,aAAa,KAAK,QAAQ;;;;;AAMvD,SAAgB,cAAc,SAEnB;AACT,QAAO,mBAAmB,CAAC,cAAc,QAAQ;;;;;AAMnD,SAAgB,UACd,OACA,eACiB;AACjB,QAAO,mBAAmB,CAAC,UAAU,OAAO,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkD5D,SAAgB,SAAS,MAAc,SAAiC;AACtE,QAAO,mBAAmB,CAAC,SAAS,MAAM,QAAQ;;;;;;;;AASpD,SAAgB,kBACd,MACA,SACS;AACT,QAAO,mBAAmB,CAAC,kBAAkB,MAAM,QAAQ;;;;;;;;AAS7D,SAAgB,SACd,QACA,aACA,SACM;AACN,QAAO,mBAAmB,CAAC,SAAS,QAAQ,aAAa,QAAQ;;;;;;;;AASnE,SAAgB,aACd,MACA,aACA,SACM;AACN,QAAO,mBAAmB,CAAC,aAAa,MAAM,aAAa,QAAQ;;;;;AAMrE,SAAgB,WAAW,SAAoD;AAC7E,QAAO,mBAAmB,CAAC,WAAW,QAAQ;;;;;;AAOhD,SAAgB,kBACd,MACA,SACQ;CAER,MAAM,2BAAW,IAAI,KAAa;CAElC,MAAM,eAAe,OAAgB;EACnC,MAAM,MAAM,GAAG,aAAa,QAAQ;AACpC,MAAI,CAAC,IAAK;AACV,OAAK,MAAM,SAAS,IAAI,MAAM,MAAM,CAClC,KAAI,eAAe,KAAK,MAAM,CAAE,UAAS,IAAI,MAAM;;AAKvD,KAAK,KAAiB,aACpB,aAAY,KAAgB;CAG9B,MAAM,WAAY,KAAoB,mBACjC,KAAoB,iBAAiB,UAAU,GAC/C,EAAE;AACP,KAAI,SAAU,UAAS,QAAQ,YAAY;AAE3C,QAAO,mBAAmB,CAAC,qBAAqB,UAAU,QAAQ;;;;;AAMpE,SAAgB,QAAQ,MAAoC;AAC1D,QAAO,mBAAmB,CAAC,QAAQ,KAAK;;;;;;;AAQ1C,SAAgB,MACd,WACA,SACM;AACN,KAAI,CAAC,WAAW,CAAC,GAAI;AACrB,oBAAmB,CAAC,MAAM,WAAW,QAAQ;;;;;;;;;AAU/C,SAAgB,GAAG,SAA6B;AAC9C,QAAO,mBAAmB,CAAC,GAAG,QAAQ;;;;;AAMxC,SAAgB,uBAAgC;AAC9C,QAAO,mBAAmB;;;;;AAM5B,MAAa,WAAW,EACtB,IAAI,WAAW;AACb,QAAO,mBAAmB;GAE7B;;;;AAKD,SAAgB,QAAQ,MAAoC;AAC1D,QAAO,mBAAmB,CAAC,QAAQ,KAAK;;;;;AAM1C,SAAgB,eACd,SAAuC,EAAE,EAC1B;AAUf,QAAO,IAAI,cAP6B;EACtC,GAHoB,WAAW;EAK/B,oBAAoB,OAAO,sBAAsB,mBAAmB;EACpE,GAAG;EACJ,CAEmC"}
@@ -11,6 +11,11 @@ declare class StyleInjector {
11
11
  /** @internal — exposed for debug utilities only */
12
12
  get _sheetManager(): SheetManager;
13
13
  constructor(config?: StyleInjectorConfig);
14
+ /**
15
+ * Check if `className` was hydrated from server-rendered styles and,
16
+ * if so, wire the cacheKey mapping. Returns true on hit.
17
+ */
18
+ private tryHydratedHit;
14
19
  /**
15
20
  * Allocate a className for a cacheKey without injecting styles yet.
16
21
  * This allows separating className allocation (render phase) from style injection (insertion phase).
@@ -1,4 +1,5 @@
1
1
  import { getEffectiveDefinition, normalizePropertyDefinition } from "../properties/index.js";
2
+ import { hashString } from "../utils/hash.js";
2
3
  import { isDevEnv } from "../utils/is-dev-env.js";
3
4
  import { parseStyle } from "../utils/styles.js";
4
5
  import { SheetManager } from "./sheet-manager.js";
@@ -6,10 +7,61 @@ import { fontFaceContentHash, formatFontFaceDeclarations } from "../font-face/in
6
7
  import { formatCounterStyleDeclarations } from "../counter-style/index.js";
7
8
  //#region src/injector/injector.ts
8
9
  /**
9
- * Generate sequential class name with format t{number}
10
+ * Generate a deterministic class name from a cache key using content hash.
11
+ * The same cache key always produces the same class name across environments.
10
12
  */
11
- function generateClassName(counter) {
12
- return `t${counter}`;
13
+ function generateClassName(cacheKey) {
14
+ return `t${hashString(cacheKey)}`;
15
+ }
16
+ const RSC_CLASS_RE = /\.(t[a-z0-9]+)\.\1/g;
17
+ /**
18
+ * Extract class names from `<style data-tasty-rsc>` tags.
19
+ * The doubled-specificity pattern `.tXXX.tXXX` makes extraction reliable.
20
+ */
21
+ function extractRSCClassNames() {
22
+ if (typeof document === "undefined") return [];
23
+ const styles = document.querySelectorAll("style[data-tasty-rsc]");
24
+ if (styles.length === 0) return [];
25
+ const classSet = /* @__PURE__ */ new Set();
26
+ for (const style of styles) {
27
+ const text = style.textContent;
28
+ if (!text) continue;
29
+ let match;
30
+ RSC_CLASS_RE.lastIndex = 0;
31
+ while ((match = RSC_CLASS_RE.exec(text)) !== null) classSet.add(match[1]);
32
+ }
33
+ return Array.from(classSet);
34
+ }
35
+ /**
36
+ * Lazily sync server-rendered class names into the client registry.
37
+ *
38
+ * Sources:
39
+ * 1. `window.__TASTY__` — pushed by SSR/RSC streaming scripts
40
+ * 2. `<style data-tasty-rsc>` tags — inline CSS emitted by RSC components
41
+ *
42
+ * Called inside `inject()` / `allocateClassName()` to pick up
43
+ * class names rendered on the server (including during SPA navigation).
44
+ */
45
+ function syncServerClasses(registry) {
46
+ if (typeof window === "undefined") return;
47
+ const classes = window.__TASTY__;
48
+ if (classes && classes.length > registry.serverClassSyncIndex) {
49
+ for (let i = registry.serverClassSyncIndex; i < classes.length; i++) registerHydratedClass(registry, classes[i]);
50
+ registry.serverClassSyncIndex = classes.length;
51
+ }
52
+ if (!registry.rscStylesScanned) {
53
+ registry.rscStylesScanned = true;
54
+ for (const cls of extractRSCClassNames()) registerHydratedClass(registry, cls);
55
+ }
56
+ }
57
+ function registerHydratedClass(registry, className) {
58
+ if (registry.rules.has(className)) return;
59
+ registry.rules.set(className, {
60
+ className,
61
+ ruleIndex: -2,
62
+ sheetIndex: -2
63
+ });
64
+ registry.refCounts.set(className, 0);
13
65
  }
14
66
  var StyleInjector = class StyleInjector {
15
67
  sheetManager;
@@ -25,6 +77,19 @@ var StyleInjector = class StyleInjector {
25
77
  this.sheetManager = new SheetManager(config);
26
78
  }
27
79
  /**
80
+ * Check if `className` was hydrated from server-rendered styles and,
81
+ * if so, wire the cacheKey mapping. Returns true on hit.
82
+ */
83
+ tryHydratedHit(registry, cacheKey, className) {
84
+ syncServerClasses(registry);
85
+ const rule = registry.rules.get(className);
86
+ if (rule && rule.ruleIndex === -2 && rule.sheetIndex === -2) {
87
+ registry.cacheKeyToClassName.set(cacheKey, className);
88
+ return true;
89
+ }
90
+ return false;
91
+ }
92
+ /**
28
93
  * Allocate a className for a cacheKey without injecting styles yet.
29
94
  * This allows separating className allocation (render phase) from style injection (insertion phase).
30
95
  */
@@ -35,7 +100,19 @@ var StyleInjector = class StyleInjector {
35
100
  className: registry.cacheKeyToClassName.get(cacheKey),
36
101
  isNewAllocation: false
37
102
  };
38
- const className = generateClassName(registry.classCounter++);
103
+ const className = generateClassName(cacheKey);
104
+ if (this.tryHydratedHit(registry, cacheKey, className)) return {
105
+ className,
106
+ isNewAllocation: false
107
+ };
108
+ if (registry.rules.get(className)) {
109
+ if (isDevEnv()) console.warn(`[tasty] Hash collision: cache keys produce the same class "${className}". Styles may be incorrect.`);
110
+ registry.cacheKeyToClassName.set(cacheKey, className);
111
+ return {
112
+ className,
113
+ isNewAllocation: false
114
+ };
115
+ }
39
116
  const placeholderRuleInfo = {
40
117
  className,
41
118
  ruleIndex: -1,
@@ -74,7 +151,17 @@ var StyleInjector = class StyleInjector {
74
151
  dispose: () => this.dispose(className, registry)
75
152
  };
76
153
  }
77
- } else className = generateClassName(registry.classCounter++);
154
+ } else if (cacheKey) {
155
+ className = generateClassName(cacheKey);
156
+ if (this.tryHydratedHit(registry, cacheKey, className)) {
157
+ registry.refCounts.set(className, (registry.refCounts.get(className) || 0) + 1);
158
+ if (registry.metrics) registry.metrics.hits++;
159
+ return {
160
+ className,
161
+ dispose: () => this.dispose(className, registry)
162
+ };
163
+ }
164
+ } else className = `t${hashString(rules.map((r) => `${r.selector}\0${r.declarations}`).join("\n"))}`;
78
165
  const rulesToInsert = rules.map((rule) => {
79
166
  let newSelector = rule.selector;
80
167
  if (rule.needsClassName) {
@@ -447,7 +534,7 @@ var StyleInjector = class StyleInjector {
447
534
  }
448
535
  }
449
536
  }
450
- static TASTY_CLASS_RE = /^t\d+$/;
537
+ static TASTY_CLASS_RE = /^t[a-z0-9]+$/;
451
538
  /**
452
539
  * Record a render-time usage hit for one or more classNames.
453
540
  * Handles space-separated multi-chunk classNames.