@tenphi/tasty 0.0.0-snapshot.27b6708 → 0.0.0-snapshot.28b6339

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 (109) hide show
  1. package/README.md +38 -54
  2. package/dist/{babel-BUQGeOXA.d.ts → babel-Zs1Q03qt.d.ts} +2 -2
  3. package/dist/chunks/async-storage-DKK-wTD4.js.map +1 -0
  4. package/dist/chunks/build-config-Mk7waOsw.js +45 -0
  5. package/dist/chunks/build-config-Mk7waOsw.js.map +1 -0
  6. package/dist/{collector-DTahQUiV.js → chunks/collector-Ducc5l3E.js} +29 -15
  7. package/dist/chunks/collector-Ducc5l3E.js.map +1 -0
  8. package/dist/chunks/config-engine-tKK7WxJO.js +331 -0
  9. package/dist/chunks/config-engine-tKK7WxJO.js.map +1 -0
  10. package/dist/chunks/css-definitions-Crstltrf.js +1229 -0
  11. package/dist/chunks/css-definitions-Crstltrf.js.map +1 -0
  12. package/dist/chunks/css-resources-Cyl_axbI.js +149 -0
  13. package/dist/chunks/css-resources-Cyl_axbI.js.map +1 -0
  14. package/dist/chunks/debug-HyyLIf6O.js +543 -0
  15. package/dist/chunks/debug-HyyLIf6O.js.map +1 -0
  16. package/dist/chunks/dsl-D7kBow--.js +1771 -0
  17. package/dist/chunks/dsl-D7kBow--.js.map +1 -0
  18. package/dist/chunks/hydration-CLdVKcH2.js +59 -0
  19. package/dist/chunks/hydration-CLdVKcH2.js.map +1 -0
  20. package/dist/chunks/react-runtime-UlZcbsEN.js +877 -0
  21. package/dist/chunks/react-runtime-UlZcbsEN.js.map +1 -0
  22. package/dist/chunks/runtime-engine-D2WOLvd6.js +3439 -0
  23. package/dist/chunks/runtime-engine-D2WOLvd6.js.map +1 -0
  24. package/dist/chunks/runtime-state-DOeH6rsG.js +138 -0
  25. package/dist/chunks/runtime-state-DOeH6rsG.js.map +1 -0
  26. package/dist/{merge-styles-oklji0KB.js → chunks/shared-utils-Bgl8FNGJ.js} +64 -24
  27. package/dist/chunks/shared-utils-Bgl8FNGJ.js.map +1 -0
  28. package/dist/chunks/style-engine-C4ZcOAPb.js +6915 -0
  29. package/dist/chunks/style-engine-C4ZcOAPb.js.map +1 -0
  30. package/dist/{css-writer-B-J87ncv.js → chunks/zero-engine-D4Rluo7u.js} +3 -3
  31. package/dist/chunks/zero-engine-D4Rluo7u.js.map +1 -0
  32. package/dist/{collector-DUaHCcTS.d.ts → collector-hl5KNH7k.d.ts} +23 -3
  33. package/dist/{config-YsxGv4tq.d.ts → config-DWL1ZYue.d.ts} +150 -55
  34. package/dist/core/index.d.ts +5 -5
  35. package/dist/core/index.js +9 -6
  36. package/dist/{index-PqN-DIpn.d.ts → index-D9N9GV24.d.ts} +129 -93
  37. package/dist/index.d.ts +5 -5
  38. package/dist/index.js +11 -922
  39. package/dist/{merge-styles-CU7JbEwg.d.ts → merge-styles-C_oMoJVd.d.ts} +2 -2
  40. package/dist/ssr/astro-client.js +1 -1
  41. package/dist/ssr/astro-middleware-extract-static.d.ts +11 -0
  42. package/dist/ssr/astro-middleware-extract-static.js +9 -0
  43. package/dist/ssr/astro-middleware-extract-static.js.map +1 -0
  44. package/dist/ssr/astro-middleware-extract.d.ts +11 -0
  45. package/dist/ssr/astro-middleware-extract.js +9 -0
  46. package/dist/ssr/astro-middleware-extract.js.map +1 -0
  47. package/dist/ssr/astro-middleware-static.d.ts +3 -1
  48. package/dist/ssr/astro-middleware.d.ts +3 -1
  49. package/dist/ssr/astro.d.ts +45 -3
  50. package/dist/ssr/astro.js +158 -15
  51. package/dist/ssr/astro.js.map +1 -1
  52. package/dist/ssr/index.d.ts +7 -7
  53. package/dist/ssr/index.js +4 -4
  54. package/dist/ssr/index.js.map +1 -1
  55. package/dist/ssr/next-config.d.ts +66 -0
  56. package/dist/ssr/next-config.js +135 -0
  57. package/dist/ssr/next-config.js.map +1 -0
  58. package/dist/ssr/next.d.ts +9 -2
  59. package/dist/ssr/next.js +27 -11
  60. package/dist/ssr/next.js.map +1 -1
  61. package/dist/static/index.d.ts +2 -2
  62. package/dist/static/index.js +1 -1
  63. package/dist/{index-Cd45t5NM.d.ts → types-jKZNpias.d.ts} +898 -803
  64. package/dist/zero/babel.d.ts +1 -1
  65. package/dist/zero/babel.js +25 -24
  66. package/dist/zero/babel.js.map +1 -1
  67. package/dist/zero/index.d.ts +1 -1
  68. package/dist/zero/index.js +1 -1
  69. package/dist/zero/next.d.ts +1 -1
  70. package/docs/README.md +5 -3
  71. package/docs/adoption.md +3 -3
  72. package/docs/ai-agents.md +83 -78
  73. package/docs/comparison.md +12 -12
  74. package/docs/configuration.md +47 -47
  75. package/docs/debug.md +19 -7
  76. package/docs/dsl.md +3 -3
  77. package/docs/getting-started.md +10 -10
  78. package/docs/injector.md +62 -25
  79. package/docs/methodology.md +3 -3
  80. package/docs/migration-v3.md +49 -49
  81. package/docs/plugins.md +37 -33
  82. package/docs/react-api.md +2 -2
  83. package/docs/runtime-benchmarks.md +351 -0
  84. package/docs/ssr.md +219 -61
  85. package/docs/styles.md +43 -11
  86. package/docs/tasty-static.md +136 -103
  87. package/package.json +80 -11
  88. package/dist/async-storage-DKK-wTD4.js.map +0 -1
  89. package/dist/collector-DTahQUiV.js.map +0 -1
  90. package/dist/config-B5kHzuNz.js +0 -12269
  91. package/dist/config-B5kHzuNz.js.map +0 -1
  92. package/dist/context-CA8YKeMn.js +0 -24
  93. package/dist/context-CA8YKeMn.js.map +0 -1
  94. package/dist/core-Dr4u1NVD.js +0 -1566
  95. package/dist/core-Dr4u1NVD.js.map +0 -1
  96. package/dist/css-writer-B-J87ncv.js.map +0 -1
  97. package/dist/format-global-rules-DklyaXv-.js +0 -22
  98. package/dist/format-global-rules-DklyaXv-.js.map +0 -1
  99. package/dist/format-rules-DKOA-6qu.js +0 -130
  100. package/dist/format-rules-DKOA-6qu.js.map +0 -1
  101. package/dist/hydrate-OeMX99We.js +0 -37
  102. package/dist/hydrate-OeMX99We.js.map +0 -1
  103. package/dist/index.js.map +0 -1
  104. package/dist/keyframes-CV8azJf3.js +0 -493
  105. package/dist/keyframes-CV8azJf3.js.map +0 -1
  106. package/dist/merge-styles-oklji0KB.js.map +0 -1
  107. package/dist/resolve-recipes-DTG81rzl.js +0 -144
  108. package/dist/resolve-recipes-DTG81rzl.js.map +0 -1
  109. /package/dist/{async-storage-DKK-wTD4.js → chunks/async-storage-DKK-wTD4.js} +0 -0
@@ -0,0 +1,1229 @@
1
+ import { F as RE_RAW_UNIT, P as RE_NUMBER, a as customFunction, f as parseStyle } from "./dsl-D7kBow--.js";
2
+ import { C as STYLE_HANDLER_MAP, k as createStyle } from "./style-engine-C4ZcOAPb.js";
3
+ //#region src/functions/index.ts
4
+ const FUNCTION_KEY = "@function";
5
+ /** Descriptor keys that are not local-variable declarations. */
6
+ const RESERVED_KEYS = new Set([
7
+ "args",
8
+ "returns",
9
+ "result"
10
+ ]);
11
+ /**
12
+ * Check if styles object has a local @function definition.
13
+ */
14
+ function hasLocalFunctions(styles) {
15
+ return FUNCTION_KEY in styles;
16
+ }
17
+ /**
18
+ * Extract local @function definitions from a styles object.
19
+ * Returns null if none (fast path).
20
+ */
21
+ function extractLocalFunctions(styles) {
22
+ const functions = styles[FUNCTION_KEY];
23
+ if (!functions || typeof functions !== "object") return null;
24
+ return functions;
25
+ }
26
+ /**
27
+ * Parse a function name token into its CSS custom name.
28
+ * Accepts `$$name`, `$name`, `--name`, or bare `name` → `--name`.
29
+ */
30
+ function parseFunctionName(token) {
31
+ if (token.startsWith("$$")) return `--${token.slice(2)}`;
32
+ if (token.startsWith("$")) return `--${token.slice(1)}`;
33
+ if (token.startsWith("--")) return token;
34
+ return `--${token}`;
35
+ }
36
+ /**
37
+ * Parse a parameter / local-variable name token into its CSS custom name.
38
+ * Accepts `$name`, `--name`, or bare `name` → `--name`.
39
+ */
40
+ function parseParamName(token) {
41
+ if (token.startsWith("$")) return `--${token.slice(1)}`;
42
+ if (token.startsWith("--")) return token;
43
+ return `--${token}`;
44
+ }
45
+ /**
46
+ * Format a single parameter (`--name <type>: default`).
47
+ */
48
+ function formatParameter(token, param) {
49
+ const name = parseParamName(token);
50
+ if (param === true) return name;
51
+ if (typeof param === "string") return param ? `${name} ${param}` : name;
52
+ let out = name;
53
+ if (param.syntax) out += ` ${param.syntax}`;
54
+ if (param.default !== void 0) out += `: ${parseStyle(param.default).output}`;
55
+ return out;
56
+ }
57
+ /**
58
+ * Format the function prelude: `@function --name(params) returns <type>`.
59
+ */
60
+ function formatFunctionPrelude(name, args, returns) {
61
+ const cssName = parseFunctionName(name);
62
+ let params = "";
63
+ if (Array.isArray(args)) params = args.map((token) => formatParameter(token, true)).join(", ");
64
+ else if (args) params = Object.entries(args).map(([token, param]) => formatParameter(token, param)).join(", ");
65
+ const returnsPart = returns ? ` returns ${returns}` : "";
66
+ return `@function ${cssName}(${params})${returnsPart}`;
67
+ }
68
+ /**
69
+ * Format the inner declarations of a @function rule (local variables + result).
70
+ */
71
+ function formatFunctionDeclarations(def) {
72
+ const parts = [];
73
+ for (const key of Object.keys(def)) {
74
+ if (RESERVED_KEYS.has(key) || !key.startsWith("$")) continue;
75
+ const value = def[key];
76
+ if (value === void 0) continue;
77
+ parts.push(`${parseParamName(key)}: ${parseStyle(value).output};`);
78
+ }
79
+ parts.push(`result: ${parseStyle(def.result).output};`);
80
+ return parts.join(" ");
81
+ }
82
+ /**
83
+ * Format a complete @function rule as CSS.
84
+ */
85
+ function formatFunctionRule(name, def) {
86
+ return `${formatFunctionPrelude(name, def.args, def.returns)} { ${formatFunctionDeclarations(def)} }`;
87
+ }
88
+ /** A key is a CSS-function key when it carries the `$`/`--` prefix. */
89
+ function isCssFunctionKey(key) {
90
+ return key.startsWith("$") || key.startsWith("--");
91
+ }
92
+ /** Discriminate a unified `functions` entry by its value type. */
93
+ function isParseFunction(value) {
94
+ return typeof value === "function";
95
+ }
96
+ /**
97
+ * Split the unified `functions` map into the two internal registries:
98
+ * - `parseFuncs`: JS parse functions (bare keys).
99
+ * - `functionDefs`: declarative CSS `@function` definitions (`$$` keys).
100
+ *
101
+ * Entries whose key prefix does not match the value type are reported via
102
+ * `onMismatch` and skipped.
103
+ */
104
+ function splitFunctions(map, onMismatch) {
105
+ const parseFuncs = {};
106
+ const functionDefs = {};
107
+ for (const [key, value] of Object.entries(map)) {
108
+ const cssKey = isCssFunctionKey(key);
109
+ if (isParseFunction(value)) {
110
+ if (cssKey) {
111
+ onMismatch?.(key, "expected-definition");
112
+ continue;
113
+ }
114
+ parseFuncs[key] = value;
115
+ } else {
116
+ if (!cssKey) {
117
+ onMismatch?.(key, "expected-parse-function");
118
+ continue;
119
+ }
120
+ functionDefs[key] = value;
121
+ }
122
+ }
123
+ return {
124
+ parseFuncs,
125
+ functionDefs
126
+ };
127
+ }
128
+ function escapeRegExp(str) {
129
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
130
+ }
131
+ const paramBareName = (token) => parseParamName(token).slice(2);
132
+ /**
133
+ * Rewrite `var(--name)` references for declared parameter/local names to a
134
+ * collision-resistant, function-scoped prefix. This guarantees that later
135
+ * value substitution only ever targets the function's own variables and can
136
+ * never accidentally match a real element-level `var(--name)` introduced by an
137
+ * argument value (e.g. the `--gap` used by Tasty units). Blanket prefix — no
138
+ * exact-collision detection.
139
+ */
140
+ function prefixDeclaredVars(template, declared, prefixOf) {
141
+ let out = template;
142
+ for (const bare of declared) {
143
+ const re = new RegExp(`var\\(\\s*--${escapeRegExp(bare)}(?![\\w-])`, "g");
144
+ out = out.replace(re, `var(--${prefixOf(bare)}`);
145
+ }
146
+ return out;
147
+ }
148
+ /**
149
+ * Replace the prefixed `var(--<prefixed>)` / `var(--<prefixed>, fallback)`
150
+ * references with their mapped values, iterating so that local variables that
151
+ * reference parameters (or other locals) resolve fully.
152
+ */
153
+ function substituteVars(template, valueMap) {
154
+ let out = template;
155
+ for (let iter = 0; iter < 30; iter++) {
156
+ let changed = false;
157
+ for (const [name, value] of valueMap) {
158
+ const re = new RegExp(`var\\(\\s*--${escapeRegExp(name)}\\s*(?:,[^)]*)?\\)`, "g");
159
+ out = out.replace(re, () => {
160
+ changed = true;
161
+ return value;
162
+ });
163
+ }
164
+ if (!changed) break;
165
+ }
166
+ return out;
167
+ }
168
+ /** Function names currently being expanded, to guard against recursion cycles. */
169
+ const inProgress = /* @__PURE__ */ new Set();
170
+ /**
171
+ * Compile a declarative `@function` definition into a parse-function closure
172
+ * that fully inlines the call into plain CSS (the `@function` polyfill).
173
+ *
174
+ * Parameters and local variables are substituted directly with their values,
175
+ * so no function-internal custom properties are emitted (collisions are
176
+ * structurally impossible). Self/mutually-recursive functions are not expanded:
177
+ * the cycle guard bails and the call is left untouched.
178
+ */
179
+ function compileFunctionClosure(name, def) {
180
+ const cssName = parseFunctionName(name);
181
+ const prefixOf = (bare) => `tf-${cssName.slice(2)}-${bare}`;
182
+ const params = [];
183
+ if (Array.isArray(def.args)) for (const token of def.args) params.push({ prefixed: prefixOf(paramBareName(token)) });
184
+ else if (def.args) for (const [token, param] of Object.entries(def.args)) {
185
+ let dflt;
186
+ if (param && typeof param === "object" && param.default !== void 0) dflt = parseStyle(param.default).output;
187
+ params.push({
188
+ prefixed: prefixOf(paramBareName(token)),
189
+ default: dflt
190
+ });
191
+ }
192
+ const localKeys = Object.keys(def).filter((key) => !RESERVED_KEYS.has(key) && key.startsWith("$"));
193
+ const declared = [...Array.isArray(def.args) ? def.args.map(paramBareName) : def.args ? Object.keys(def.args).map(paramBareName) : [], ...localKeys.map(paramBareName)];
194
+ let built = false;
195
+ let resultTemplate = "";
196
+ const localTemplates = [];
197
+ const build = () => {
198
+ built = true;
199
+ resultTemplate = prefixDeclaredVars(parseStyle(def.result).output, declared, prefixOf);
200
+ for (const key of localKeys) {
201
+ const value = def[key];
202
+ if (value === void 0) continue;
203
+ localTemplates.push({
204
+ prefixed: prefixOf(paramBareName(key)),
205
+ value: prefixDeclaredVars(parseStyle(value).output, declared, prefixOf)
206
+ });
207
+ }
208
+ };
209
+ return (groups) => {
210
+ if (inProgress.has(cssName)) return "";
211
+ inProgress.add(cssName);
212
+ try {
213
+ if (!built) build();
214
+ const valueMap = /* @__PURE__ */ new Map();
215
+ params.forEach((param, index) => {
216
+ const arg = groups[index]?.output?.trim();
217
+ valueMap.set(param.prefixed, arg || param.default || "");
218
+ });
219
+ for (const local of localTemplates) valueMap.set(local.prefixed, local.value);
220
+ return substituteVars(resultTemplate, valueMap);
221
+ } finally {
222
+ inProgress.delete(cssName);
223
+ }
224
+ };
225
+ }
226
+ /** CSS function name -> serialized definition, to dedupe polyfill registration. */
227
+ const registeredFunctionDefs = /* @__PURE__ */ new Map();
228
+ /**
229
+ * Register a declarative `@function` as a parse-function closure so the parser
230
+ * inlines its call sites. Deduplicated by name + definition; a later, different
231
+ * definition of the same name overrides the previous one (local wins).
232
+ */
233
+ function registerFunctionPolyfill(name, definition) {
234
+ const cssName = parseFunctionName(name);
235
+ const serialized = JSON.stringify(definition);
236
+ if (registeredFunctionDefs.get(cssName) === serialized) return;
237
+ registeredFunctionDefs.set(cssName, serialized);
238
+ customFunction(cssName, compileFunctionClosure(name, definition));
239
+ }
240
+ /** Register all local `@function` definitions from a styles object (polyfill). */
241
+ function registerLocalFunctionPolyfills(styles) {
242
+ const local = extractLocalFunctions(styles);
243
+ if (!local) return;
244
+ for (const [name, definition] of Object.entries(local)) registerFunctionPolyfill(name, definition);
245
+ }
246
+ /** Clear polyfill registration bookkeeping (used by resetConfig in tests). */
247
+ function resetFunctionPolyfills() {
248
+ registeredFunctionDefs.clear();
249
+ }
250
+ //#endregion
251
+ //#region src/properties/defaults.ts
252
+ /**
253
+ * Default properties shipped with Tasty.
254
+ *
255
+ * Kept with the property formatter so runtime and build-time configuration
256
+ * paths can share the same list without importing the browser runtime.
257
+ */
258
+ const DEFAULT_PROPERTIES = {
259
+ "#tasty-second-fill": {
260
+ inherits: false,
261
+ initialValue: "transparent"
262
+ },
263
+ "#current": {
264
+ inherits: true,
265
+ initialValue: "currentcolor"
266
+ },
267
+ "#white": {
268
+ inherits: true,
269
+ initialValue: "rgb(255 255 255)"
270
+ },
271
+ "#black": {
272
+ inherits: true,
273
+ initialValue: "rgb(0 0 0)"
274
+ },
275
+ "#clear": {
276
+ inherits: true,
277
+ initialValue: "transparent"
278
+ },
279
+ "#border": {
280
+ inherits: true,
281
+ initialValue: "rgb(0 0 0)"
282
+ },
283
+ $gap: {
284
+ syntax: "<length>",
285
+ inherits: true,
286
+ initialValue: "4px"
287
+ },
288
+ $radius: {
289
+ syntax: "<length>",
290
+ inherits: true,
291
+ initialValue: "6px"
292
+ },
293
+ "$border-width": {
294
+ syntax: "<length>",
295
+ inherits: true,
296
+ initialValue: "1px"
297
+ },
298
+ "$outline-width": {
299
+ syntax: "<length>",
300
+ inherits: true,
301
+ initialValue: "3px"
302
+ },
303
+ $transition: {
304
+ syntax: "<time>",
305
+ inherits: true,
306
+ initialValue: "80ms"
307
+ },
308
+ "$sharp-radius": {
309
+ syntax: "<length>",
310
+ inherits: true,
311
+ initialValue: "0px"
312
+ },
313
+ "$bold-font-weight": {
314
+ syntax: "<number>",
315
+ inherits: true,
316
+ initialValue: "700"
317
+ },
318
+ "$font-sans-fallback": {
319
+ syntax: "*",
320
+ inherits: true,
321
+ initialValue: "system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", sans-serif"
322
+ },
323
+ "$font-mono-fallback": {
324
+ syntax: "*",
325
+ inherits: true,
326
+ initialValue: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace"
327
+ }
328
+ };
329
+ //#endregion
330
+ //#region src/properties/index.ts
331
+ const PROPERTIES_KEY = "@property";
332
+ /**
333
+ * Valid CSS custom property name pattern (after the -- prefix).
334
+ * Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.
335
+ */
336
+ const VALID_PROPERTY_NAME_PATTERN = /^[a-z_][a-z0-9-_]*$/i;
337
+ /**
338
+ * Validate a CSS custom property name (the part after --).
339
+ * Returns true if the name is valid for use as a CSS custom property.
340
+ */
341
+ function isValidPropertyName(name) {
342
+ return VALID_PROPERTY_NAME_PATTERN.test(name);
343
+ }
344
+ /**
345
+ * Check if styles object has local @property definition.
346
+ * Fast path: single property lookup.
347
+ */
348
+ function hasLocalProperties(styles) {
349
+ return PROPERTIES_KEY in styles;
350
+ }
351
+ /**
352
+ * Extract local @property from styles object.
353
+ * Returns null if no local properties (fast path).
354
+ */
355
+ function extractLocalProperties(styles) {
356
+ const properties = styles[PROPERTIES_KEY];
357
+ if (!properties || typeof properties !== "object") return null;
358
+ return properties;
359
+ }
360
+ /**
361
+ * Parse a property token name and return the CSS property name and whether it's a color.
362
+ * Supports tasty token syntax and validates the property name.
363
+ *
364
+ * Token formats:
365
+ * - `$name` → { cssName: '--name', isColor: false }
366
+ * - `#name` → { cssName: '--name-color', isColor: true }
367
+ * - `--name` → { cssName: '--name', isColor: false } (legacy, auto-detect color by suffix)
368
+ * - `name` → { cssName: '--name', isColor: false } (legacy)
369
+ *
370
+ * @param token - The property token to parse
371
+ * @returns Parsed result with cssName, isColor, isValid, and optional error
372
+ */
373
+ function parsePropertyToken(token) {
374
+ if (!token || typeof token !== "string") return {
375
+ cssName: "",
376
+ isColor: false,
377
+ isValid: false,
378
+ error: "Property token must be a non-empty string"
379
+ };
380
+ let name;
381
+ let isColor;
382
+ if (token.startsWith("$")) {
383
+ name = token.slice(1);
384
+ isColor = false;
385
+ } else if (token.startsWith("#")) {
386
+ name = token.slice(1);
387
+ isColor = true;
388
+ } else if (token.startsWith("--")) {
389
+ name = token.slice(2);
390
+ isColor = token.endsWith("-color");
391
+ } else {
392
+ name = token;
393
+ isColor = token.endsWith("-color");
394
+ }
395
+ if (!name) return {
396
+ cssName: "",
397
+ isColor,
398
+ isValid: false,
399
+ error: "Property name cannot be empty"
400
+ };
401
+ if (!isValidPropertyName(name)) return {
402
+ cssName: "",
403
+ isColor,
404
+ isValid: false,
405
+ error: `Invalid property name "${name}". Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.`
406
+ };
407
+ let cssName;
408
+ if (token.startsWith("#")) cssName = `--${name}-color`;
409
+ else cssName = `--${name}`;
410
+ return {
411
+ cssName,
412
+ isColor,
413
+ isValid: true
414
+ };
415
+ }
416
+ /**
417
+ * Normalize a property definition to a consistent string representation.
418
+ * Used for comparing definitions to detect type conflicts.
419
+ *
420
+ * Only `syntax` and `inherits` are compared — `initialValue` is intentionally
421
+ * excluded because different components may set different defaults for the
422
+ * same typed property (e.g. auto-inferred `0px` vs explicit `6px`).
423
+ *
424
+ * Keys are sorted alphabetically to ensure consistent comparison:
425
+ * { inherits: true, syntax: '<color>' } === { syntax: '<color>', inherits: true }
426
+ */
427
+ function normalizePropertyDefinition(def) {
428
+ const normalized = {};
429
+ if (def.inherits !== void 0) normalized.inherits = def.inherits;
430
+ if (def.syntax !== void 0) normalized.syntax = def.syntax;
431
+ return JSON.stringify(normalized);
432
+ }
433
+ /**
434
+ * Get the effective property definition for a token.
435
+ * For color tokens (#name), auto-sets syntax to '<color>' and defaults initialValue to 'transparent'.
436
+ *
437
+ * @param token - Property token ($name, #name, --name, or plain name)
438
+ * @param userDefinition - User-provided definition options
439
+ * @returns Effective definition with cssName, definition, isValid, and optional error
440
+ */
441
+ function getEffectiveDefinition(token, userDefinition) {
442
+ const parsed = parsePropertyToken(token);
443
+ if (!parsed.isValid) return {
444
+ cssName: "",
445
+ definition: userDefinition,
446
+ isValid: false,
447
+ error: parsed.error
448
+ };
449
+ if (parsed.isColor) return {
450
+ cssName: parsed.cssName,
451
+ definition: {
452
+ syntax: "<color>",
453
+ inherits: userDefinition.inherits,
454
+ initialValue: userDefinition.initialValue ?? "transparent"
455
+ },
456
+ isValid: true
457
+ };
458
+ return {
459
+ cssName: parsed.cssName,
460
+ definition: userDefinition,
461
+ isValid: true
462
+ };
463
+ }
464
+ const UNIT_TO_SYNTAX = {};
465
+ const LENGTH_UNITS = [
466
+ "px",
467
+ "em",
468
+ "rem",
469
+ "vw",
470
+ "vh",
471
+ "vmin",
472
+ "vmax",
473
+ "ch",
474
+ "ex",
475
+ "cap",
476
+ "ic",
477
+ "lh",
478
+ "rlh",
479
+ "svw",
480
+ "svh",
481
+ "lvw",
482
+ "lvh",
483
+ "dvw",
484
+ "dvh",
485
+ "cqw",
486
+ "cqh",
487
+ "cqi",
488
+ "cqb",
489
+ "cqmin",
490
+ "cqmax"
491
+ ];
492
+ const ANGLE_UNITS = [
493
+ "deg",
494
+ "rad",
495
+ "grad",
496
+ "turn"
497
+ ];
498
+ const TIME_UNITS = ["ms", "s"];
499
+ for (const u of LENGTH_UNITS) UNIT_TO_SYNTAX[u] = {
500
+ syntax: "<length-percentage>",
501
+ initialValue: "0px"
502
+ };
503
+ UNIT_TO_SYNTAX["%"] = {
504
+ syntax: "<length-percentage>",
505
+ initialValue: "0px"
506
+ };
507
+ for (const u of ANGLE_UNITS) UNIT_TO_SYNTAX[u] = {
508
+ syntax: "<angle>",
509
+ initialValue: "0deg"
510
+ };
511
+ for (const u of TIME_UNITS) UNIT_TO_SYNTAX[u] = {
512
+ syntax: "<time>",
513
+ initialValue: "0s"
514
+ };
515
+ /**
516
+ * Infer CSS @property syntax from a concrete value.
517
+ * Detects numeric types: \<number\>, \<length-percentage\>, \<angle\>, \<time\>.
518
+ * Length and percentage values both map to \<length-percentage\> for maximum flexibility.
519
+ * Color properties are handled separately via the #name token convention
520
+ * (--name-color gets \<color\> syntax automatically in getEffectiveDefinition).
521
+ *
522
+ * @param value - The CSS value to infer from (e.g. '10px', '1', '45deg')
523
+ * @returns Inferred syntax and initial value, or null if not inferable
524
+ */
525
+ function inferSyntaxFromValue(value) {
526
+ if (!value || typeof value !== "string") return null;
527
+ const trimmed = value.trim();
528
+ if (!trimmed) return null;
529
+ if (RE_NUMBER.test(trimmed)) {
530
+ if (parseFloat(trimmed) === 0) return null;
531
+ return {
532
+ syntax: "<number>",
533
+ initialValue: "0"
534
+ };
535
+ }
536
+ const unitMatch = trimmed.match(RE_RAW_UNIT);
537
+ if (unitMatch) {
538
+ const mapping = UNIT_TO_SYNTAX[unitMatch[2]];
539
+ if (mapping) return mapping;
540
+ }
541
+ return null;
542
+ }
543
+ //#endregion
544
+ //#region src/utils/hash.ts
545
+ /**
546
+ * Fast string hash (djb2) for deduplication keys.
547
+ * Not cryptographic — just collision-resistant enough for cache keys.
548
+ */
549
+ function hashString(str) {
550
+ let hash = 5381;
551
+ for (let i = 0; i < str.length; i++) hash = (hash << 5) + hash + str.charCodeAt(i) | 0;
552
+ return (hash >>> 0).toString(36);
553
+ }
554
+ //#endregion
555
+ //#region src/utils/name-prefix.ts
556
+ /**
557
+ * Name prefix utilities for generated identifiers.
558
+ *
559
+ * Tasty generates three kinds of identifiers from content hashes:
560
+ * - class names (used in DOM `class` attribute)
561
+ * - keyframe names (used in CSS `animation`)
562
+ * - counter-style names (used in CSS `list-style-type`)
563
+ *
564
+ * All three derive from a single configurable prefix so that an app
565
+ * can namespace every identifier under one string. Discriminator letters
566
+ * (`k`, `c`) keep the three kinds visually distinct in devtools — they
567
+ * are not required for correctness (CSS keeps these in separate
568
+ * namespaces), only for readability.
569
+ *
570
+ * The runtime / SSR / RSC paths must agree on the prefix; otherwise the
571
+ * client-side hash for a given style will not match the server-rendered
572
+ * class and hydration breaks. The zero-runtime build path uses a
573
+ * different default (`'ts'`) so its classes can't collide with runtime
574
+ * (`'t'`) classes when both are loaded on the same page.
575
+ */
576
+ /** Default prefix used by the runtime / SSR / RSC paths. */
577
+ const DEFAULT_NAME_PREFIX = "t";
578
+ /** Default prefix used by the zero-runtime (`tastyStatic`) build path. */
579
+ const DEFAULT_ZERO_NAME_PREFIX = "ts";
580
+ /**
581
+ * Allowed shape: starts with a letter or underscore, then letters/
582
+ * digits/underscore/hyphen. Length capped at 32 to keep generated
583
+ * names sane. Matches the CSS identifier rules for the common case
584
+ * while keeping the surface conservative.
585
+ */
586
+ const NAME_PREFIX_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_-]{0,31}$/;
587
+ /**
588
+ * Validate a `namePrefix` value.
589
+ * Throws a TypeError with a descriptive message on invalid input so
590
+ * misconfiguration fails loudly at `configure()` time rather than
591
+ * surfacing later as broken hydration.
592
+ */
593
+ function validateNamePrefix(prefix) {
594
+ if (typeof prefix !== "string") throw new TypeError(`[Tasty] namePrefix must be a string, got ${typeof prefix}.`);
595
+ if (!NAME_PREFIX_PATTERN.test(prefix)) throw new TypeError(`[Tasty] namePrefix "${prefix}" is invalid. It must start with a letter (a-z, A-Z) or "_", contain only letters, digits, "_" or "-", and be 1-32 characters long. Examples: "t", "ts", "myapp-", "_foo".`);
596
+ }
597
+ /**
598
+ * Build a class name: `${prefix}${hash}`.
599
+ * The hash is appended verbatim — supply a separator inside the prefix
600
+ * itself if you want one (e.g. `'myapp-'`).
601
+ */
602
+ function makeClassName(prefix, hash) {
603
+ return `${prefix}${hash}`;
604
+ }
605
+ /**
606
+ * Build a keyframe name: `${prefix}k${suffix}`.
607
+ * The `k` discriminator keeps keyframe names visually distinct from
608
+ * class names sharing the same prefix. `suffix` is typically a content
609
+ * hash but may be a counter for ad-hoc allocation.
610
+ */
611
+ function makeKeyframeName(prefix, suffix) {
612
+ return `${prefix}k${suffix}`;
613
+ }
614
+ /**
615
+ * Build a counter-style name: `${prefix}c${suffix}`.
616
+ * The `c` discriminator keeps counter-style names visually distinct
617
+ * from class names sharing the same prefix.
618
+ */
619
+ function makeCounterStyleName(prefix, suffix) {
620
+ return `${prefix}c${suffix}`;
621
+ }
622
+ /** Escape a string for safe inclusion in a regex literal. */
623
+ function escapeRegex(str) {
624
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
625
+ }
626
+ /**
627
+ * Regex matching any tasty class for the given prefix.
628
+ * Used by the runtime GC's DOM scan and class-allocation bookkeeping.
629
+ */
630
+ function tastyClassRegex(prefix) {
631
+ return new RegExp(`^${escapeRegex(prefix)}[a-z0-9]+$`);
632
+ }
633
+ /**
634
+ * Global regex extracting tasty class names from RSC-inlined CSS.
635
+ * Looks for the doubled-specificity pattern `.cls.cls` that
636
+ * `formatRules()` always emits, which makes extraction reliable.
637
+ */
638
+ function rscClassRegexGlobal(prefix) {
639
+ return new RegExp(`\\.(${escapeRegex(prefix)}[a-z0-9]+)\\.\\1`, "g");
640
+ }
641
+ //#endregion
642
+ //#region src/properties/property-type-resolver.ts
643
+ /**
644
+ * PropertyTypeResolver
645
+ *
646
+ * Automatically infers CSS @property types from custom property values.
647
+ * Supports deferred resolution for var() reference chains of arbitrary depth.
648
+ */
649
+ /**
650
+ * One `--name: value` declaration, with the surrounding whitespace dropped.
651
+ *
652
+ * The value is anchored on `\S` at both ends so no two quantifiers can claim
653
+ * the same character. A lazy `(.+?)\s*$` is quadratic on a value of nothing
654
+ * but spaces — every split point gets tried (CodeQL `js/polynomial-redos`).
655
+ */
656
+ const CUSTOM_PROP_DECL = /^\s*(--[a-z0-9_-]+)\s*:\s*(\S|\S.*\S)\s*$/i;
657
+ const SINGLE_VAR_REF = /^var\((--[a-z0-9_-]+)\)$/i;
658
+ var PropertyTypeResolver = class {
659
+ /** propName → the prop it depends on */
660
+ pendingDeps = /* @__PURE__ */ new Map();
661
+ /** propName → list of props waiting on it */
662
+ reverseDeps = /* @__PURE__ */ new Map();
663
+ /**
664
+ * Scan CSS declarations and auto-register @property for custom properties
665
+ * whose types can be inferred from their values.
666
+ */
667
+ scanDeclarations(declarations, isPropertyDefined, registerProperty) {
668
+ if (!declarations.includes("--")) return;
669
+ const parts = declarations.split(/;+/);
670
+ for (const part of parts) {
671
+ if (!part.trim()) continue;
672
+ const match = CUSTOM_PROP_DECL.exec(part);
673
+ if (!match) continue;
674
+ const propName = match[1];
675
+ const value = match[2].trim();
676
+ if (isPropertyDefined(propName)) continue;
677
+ if (propName.endsWith("-color")) {
678
+ registerProperty(propName, "<color>", "transparent");
679
+ continue;
680
+ }
681
+ if (propName.endsWith("-line-height")) {
682
+ registerProperty(propName, "<number> | <length-percentage>", "0");
683
+ continue;
684
+ }
685
+ if (propName.endsWith("-opacity")) {
686
+ registerProperty(propName, "<number> | <percentage>", "0");
687
+ continue;
688
+ }
689
+ const varMatch = SINGLE_VAR_REF.exec(value);
690
+ if (varMatch) {
691
+ const depName = varMatch[1];
692
+ this.addDependency(propName, depName);
693
+ continue;
694
+ }
695
+ if (this.isComplexValue(value)) continue;
696
+ const inferred = inferSyntaxFromValue(value);
697
+ if (!inferred) continue;
698
+ this.resolve(propName, inferred.syntax, inferred.initialValue, isPropertyDefined, registerProperty);
699
+ }
700
+ }
701
+ addDependency(propName, depName) {
702
+ if (propName === depName) return;
703
+ this.pendingDeps.set(propName, depName);
704
+ let dependents = this.reverseDeps.get(depName);
705
+ if (!dependents) {
706
+ dependents = [];
707
+ this.reverseDeps.set(depName, dependents);
708
+ }
709
+ if (!dependents.includes(propName)) dependents.push(propName);
710
+ }
711
+ resolve(propName, syntax, initialValue, isPropertyDefined, registerProperty, resolving) {
712
+ if (!resolving) resolving = /* @__PURE__ */ new Set();
713
+ if (resolving.has(propName)) return;
714
+ resolving.add(propName);
715
+ if (!isPropertyDefined(propName)) registerProperty(propName, syntax, initialValue);
716
+ const dependents = this.reverseDeps.get(propName);
717
+ if (dependents) {
718
+ this.reverseDeps.delete(propName);
719
+ for (const dependent of dependents) {
720
+ this.pendingDeps.delete(dependent);
721
+ if (isPropertyDefined(dependent)) continue;
722
+ this.resolve(dependent, syntax, initialValue, isPropertyDefined, registerProperty, resolving);
723
+ }
724
+ }
725
+ }
726
+ isComplexValue(value) {
727
+ if (value.includes("calc(")) return true;
728
+ const firstVar = value.indexOf("var(");
729
+ if (firstVar === -1) return false;
730
+ if (value.indexOf("var(", firstVar + 4) !== -1) return true;
731
+ return !SINGLE_VAR_REF.test(value);
732
+ }
733
+ };
734
+ //#endregion
735
+ //#region src/font-face/index.ts
736
+ const FONT_FACE_KEY = "@font-face";
737
+ /**
738
+ * Check if styles object has local @font-face definition.
739
+ */
740
+ function hasLocalFontFace(styles) {
741
+ return FONT_FACE_KEY in styles;
742
+ }
743
+ /**
744
+ * Extract local @font-face from styles object.
745
+ * Returns null if no local font faces (fast path).
746
+ */
747
+ function extractLocalFontFace(styles) {
748
+ const fontFace = styles[FONT_FACE_KEY];
749
+ if (!fontFace || typeof fontFace !== "object") return null;
750
+ return fontFace;
751
+ }
752
+ const FONT_FACE_DESCRIPTOR_MAP = {
753
+ fontWeight: "font-weight",
754
+ fontStyle: "font-style",
755
+ fontStretch: "font-stretch",
756
+ fontDisplay: "font-display",
757
+ unicodeRange: "unicode-range",
758
+ ascentOverride: "ascent-override",
759
+ descentOverride: "descent-override",
760
+ lineGapOverride: "line-gap-override",
761
+ sizeAdjust: "size-adjust",
762
+ fontFeatureSettings: "font-feature-settings",
763
+ fontVariationSettings: "font-variation-settings"
764
+ };
765
+ /**
766
+ * Format the inner declarations of a @font-face rule (no wrapper).
767
+ * Used by the injector which needs selector and declarations separately.
768
+ */
769
+ function formatFontFaceDeclarations(family, descriptors) {
770
+ const parts = [];
771
+ parts.push(`font-family: "${family}";`);
772
+ parts.push(`src: ${descriptors.src};`);
773
+ for (const [key, cssName] of Object.entries(FONT_FACE_DESCRIPTOR_MAP)) {
774
+ const value = descriptors[key];
775
+ if (value !== void 0) parts.push(`${cssName}: ${value};`);
776
+ }
777
+ return parts.join(" ");
778
+ }
779
+ /**
780
+ * Format a single @font-face rule as CSS.
781
+ */
782
+ function formatFontFaceRule(family, descriptors) {
783
+ return `@font-face { ${formatFontFaceDeclarations(family, descriptors)} }`;
784
+ }
785
+ /**
786
+ * Generate a content hash for deduplication of a single font-face rule.
787
+ */
788
+ function fontFaceContentHash(family, descriptors) {
789
+ return JSON.stringify({
790
+ family,
791
+ ...descriptors
792
+ });
793
+ }
794
+ //#endregion
795
+ //#region src/counter-style/index.ts
796
+ const COUNTER_STYLE_KEY = "@counter-style";
797
+ /**
798
+ * Check if styles object has local @counter-style definition.
799
+ */
800
+ function hasLocalCounterStyle(styles) {
801
+ return COUNTER_STYLE_KEY in styles;
802
+ }
803
+ /**
804
+ * Extract local @counter-style from styles object.
805
+ * Returns null if no local counter styles (fast path).
806
+ */
807
+ function extractLocalCounterStyle(styles) {
808
+ const counterStyle = styles[COUNTER_STYLE_KEY];
809
+ if (!counterStyle || typeof counterStyle !== "object") return null;
810
+ return counterStyle;
811
+ }
812
+ const COUNTER_STYLE_DESCRIPTOR_MAP = {
813
+ system: "system",
814
+ symbols: "symbols",
815
+ additiveSymbols: "additive-symbols",
816
+ prefix: "prefix",
817
+ suffix: "suffix",
818
+ negative: "negative",
819
+ range: "range",
820
+ pad: "pad",
821
+ fallback: "fallback",
822
+ speakAs: "speak-as"
823
+ };
824
+ /**
825
+ * Format the inner declarations of a @counter-style rule (no wrapper).
826
+ * Used by the injector which needs selector and declarations separately.
827
+ */
828
+ function formatCounterStyleDeclarations(descriptors) {
829
+ const parts = [];
830
+ for (const [key, cssName] of Object.entries(COUNTER_STYLE_DESCRIPTOR_MAP)) {
831
+ const value = descriptors[key];
832
+ if (value !== void 0) parts.push(`${cssName}: ${value};`);
833
+ }
834
+ return parts.join(" ");
835
+ }
836
+ /**
837
+ * Format a @counter-style rule as CSS.
838
+ */
839
+ function formatCounterStyleRule(name, descriptors) {
840
+ return `@counter-style ${name} { ${formatCounterStyleDeclarations(descriptors)} }`;
841
+ }
842
+ //#endregion
843
+ //#region src/keyframes/index.ts
844
+ const KEYFRAMES_KEY = "@keyframes";
845
+ /**
846
+ * Pattern to extract animation names from CSS animation property values.
847
+ * Animation name is typically the first identifier in the shorthand.
848
+ * Handles: "fadeIn 300ms ease-in", "pulse 1s infinite", etc.
849
+ *
850
+ * CSS animation shorthand order (all optional except name):
851
+ * animation: name | duration | timing | delay | iteration | direction | fill-mode | play-state
852
+ *
853
+ * Animation names must:
854
+ * - Start with a letter, underscore, or hyphen (but not a digit or CSS keyword)
855
+ * - Not be CSS keywords: none, initial, inherit, unset, revert
856
+ */
857
+ const NON_NAME_KEYWORDS = new Set("none initial inherit unset revert auto normal running paused reverse alternate alternate-reverse forwards backwards both ease linear ease-in ease-out ease-in-out step-start step-end".split(" "));
858
+ /**
859
+ * Check if styles object has local @keyframes definition.
860
+ * Fast path: single property lookup.
861
+ */
862
+ function hasLocalKeyframes(styles) {
863
+ return KEYFRAMES_KEY in styles;
864
+ }
865
+ /**
866
+ * Extract local @keyframes from styles object.
867
+ * Returns null if no local keyframes (fast path).
868
+ */
869
+ function extractLocalKeyframes(styles) {
870
+ const keyframes = styles[KEYFRAMES_KEY];
871
+ if (!keyframes || typeof keyframes !== "object") return null;
872
+ return keyframes;
873
+ }
874
+ /**
875
+ * Merge local and global keyframes.
876
+ * Local keyframes take priority over global.
877
+ * Returns null if no keyframes exist (fast path).
878
+ */
879
+ function mergeKeyframes(local, global) {
880
+ if (!local && !global) return null;
881
+ if (!local) return global;
882
+ if (!global) return local;
883
+ return {
884
+ ...global,
885
+ ...local
886
+ };
887
+ }
888
+ /**
889
+ * Extract animation name from a single animation value.
890
+ * Returns null if no valid name found.
891
+ *
892
+ * Examples:
893
+ * - "fadeIn 300ms ease-in" → "fadeIn"
894
+ * - "1s pulse infinite" → "pulse" (name can be anywhere)
895
+ * - "none" → null (CSS keyword)
896
+ * - "300ms ease-in" → null (no name, just duration/timing)
897
+ */
898
+ function extractAnimationNameFromValue(value) {
899
+ for (const part of value.trim().split(/\s+/)) {
900
+ const lower = part.toLowerCase();
901
+ if (NON_NAME_KEYWORDS.has(lower)) continue;
902
+ if (part === "infinite" || /^(?:-?[\d.]+m?s|\d+|(?:cubic-bezier|steps)\()/i.test(part)) continue;
903
+ const match = /^([a-zA-Z_-][a-zA-Z0-9_-]*)/.exec(part);
904
+ if (match) return match[1];
905
+ }
906
+ return null;
907
+ }
908
+ /**
909
+ * Extract all animation names from an animation property value.
910
+ * Handles multiple animations separated by commas.
911
+ *
912
+ * Example: "fadeIn 300ms, slideIn 500ms ease-out" → ["fadeIn", "slideIn"]
913
+ */
914
+ function collectAnimationNamesFromValue(value, names) {
915
+ for (const animation of value.split(",")) {
916
+ const name = extractAnimationNameFromValue(animation);
917
+ if (name) names.add(name);
918
+ }
919
+ }
920
+ /**
921
+ * Extract animation names from a style value (handles mappings and arrays).
922
+ */
923
+ function extractAnimationNamesFromStyleValue(value, names) {
924
+ if (typeof value === "string") collectAnimationNamesFromValue(value, names);
925
+ else if (Array.isArray(value)) for (const v of value) extractAnimationNamesFromStyleValue(v, names);
926
+ else if (value && typeof value === "object") for (const v of Object.values(value)) extractAnimationNamesFromStyleValue(v, names);
927
+ }
928
+ function collectAnimationNamesFromStyles(styles, names) {
929
+ if ("animation" in styles) extractAnimationNamesFromStyleValue(styles.animation, names);
930
+ if ("animationName" in styles) extractAnimationNamesFromStyleValue(styles.animationName, names);
931
+ for (const [key, value] of Object.entries(styles)) {
932
+ if (key === "$" || key === KEYFRAMES_KEY) continue;
933
+ if ((key.startsWith("&") || key.startsWith(".") || /^[A-Z]/.test(key)) && value && typeof value === "object") collectAnimationNamesFromStyles(value, names);
934
+ }
935
+ }
936
+ /**
937
+ * Extract all animation names referenced in styles.
938
+ * Scans 'animation' and 'animationName' properties including in state mappings.
939
+ * Returns empty set if no animation properties found (fast path).
940
+ */
941
+ function extractAnimationNamesFromStyles(styles) {
942
+ const names = /* @__PURE__ */ new Set();
943
+ collectAnimationNamesFromStyles(styles, names);
944
+ return names;
945
+ }
946
+ /**
947
+ * The name a local `@keyframes` block is emitted under.
948
+ *
949
+ * Content-addressed, and a pure function of what was authored: the same steps
950
+ * always produce the same name, and different steps under the same authored
951
+ * name cannot collide. That is what lets the server and the client agree —
952
+ * a name handed out by whichever injector happened to see the definition first
953
+ * depends on run order, so the two would disagree on both the animation name
954
+ * and the class that references it.
955
+ */
956
+ function resolveKeyframesName(authored, steps) {
957
+ return `${authored}-${hashString(JSON.stringify(steps))}`;
958
+ }
959
+ /**
960
+ * The emitted name for every local `@keyframes` in `styles`, by authored name.
961
+ * Pure — the server, the RSC pass and the client all reach the same answer.
962
+ */
963
+ function resolveKeyframesNames(used) {
964
+ const names = /* @__PURE__ */ new Map();
965
+ for (const [authored, steps] of Object.entries(used)) names.set(authored, resolveKeyframesName(authored, steps));
966
+ return names;
967
+ }
968
+ /** Replace local animation names and collect the definitions each rule uses. */
969
+ function replaceAnimationNames(declarations, nameMap, usedNames) {
970
+ if (nameMap.size === 0 || !declarations.includes("animation")) return declarations;
971
+ const parts = declarations.split(";");
972
+ let modified = false;
973
+ for (let i = 0; i < parts.length; i++) {
974
+ const part = parts[i];
975
+ const colonIdx = part.indexOf(":");
976
+ if (colonIdx === -1) continue;
977
+ const prop = part.slice(0, colonIdx).trim().toLowerCase();
978
+ if (prop === "animation" || prop === "animation-name") {
979
+ const prefix = part.slice(0, colonIdx + 1);
980
+ let value = part.slice(colonIdx + 1);
981
+ for (const original of nameMap.keys()) if (findWord(value, original) !== -1) usedNames.add(original);
982
+ for (const [original, injected] of nameMap) {
983
+ if (original === injected) continue;
984
+ const next = replaceWord(value, original, injected);
985
+ if (next !== value) {
986
+ value = next;
987
+ modified = true;
988
+ }
989
+ }
990
+ parts[i] = prefix + value;
991
+ }
992
+ }
993
+ return modified ? parts.join(";") : declarations;
994
+ }
995
+ /**
996
+ * Replace a word in a string (word boundary aware, no regex).
997
+ */
998
+ function replaceWord(str, word, replacement) {
999
+ let result = str;
1000
+ let idx = 0;
1001
+ while ((idx = findWord(result, word, idx)) !== -1) {
1002
+ result = result.slice(0, idx) + replacement + result.slice(idx + word.length);
1003
+ idx += replacement.length;
1004
+ }
1005
+ return result;
1006
+ }
1007
+ function findWord(str, word, from = 0) {
1008
+ let idx = from;
1009
+ while ((idx = str.indexOf(word, idx)) !== -1) {
1010
+ const before = idx === 0 ? " " : str[idx - 1];
1011
+ const end = idx + word.length;
1012
+ const after = end === str.length ? " " : str[end];
1013
+ if (!/[\w-]/.test(before + after)) return idx;
1014
+ idx = end;
1015
+ }
1016
+ return -1;
1017
+ }
1018
+ /**
1019
+ * Filter keyframes to only those that are actually used.
1020
+ * Returns null if no keyframes are used (fast path).
1021
+ */
1022
+ function filterUsedKeyframes(keyframes, usedNames) {
1023
+ if (!keyframes || usedNames.size === 0) return null;
1024
+ const used = {};
1025
+ let hasAny = false;
1026
+ for (const name of usedNames) if (keyframes[name]) {
1027
+ used[name] = keyframes[name];
1028
+ hasAny = true;
1029
+ }
1030
+ return hasAny ? used : null;
1031
+ }
1032
+ //#endregion
1033
+ //#region src/ssr/format-property.ts
1034
+ /**
1035
+ * Format a single @property rule as a CSS string.
1036
+ *
1037
+ * Returns the full `@property --name { ... }` text, or empty string
1038
+ * if the token is invalid.
1039
+ */
1040
+ function formatPropertyCSS(token, definition) {
1041
+ const result = getEffectiveDefinition(token, definition);
1042
+ if (!result.isValid) return "";
1043
+ return buildPropertyRule(result.cssName, result.definition);
1044
+ }
1045
+ function buildPropertyRule(cssName, definition) {
1046
+ const parts = [];
1047
+ if (definition.syntax != null) {
1048
+ let syntax = String(definition.syntax).trim();
1049
+ if (!/^['"]/u.test(syntax)) syntax = `"${syntax}"`;
1050
+ parts.push(`syntax: ${syntax};`);
1051
+ }
1052
+ const inherits = definition.inherits ?? true;
1053
+ parts.push(`inherits: ${inherits ? "true" : "false"};`);
1054
+ if (definition.initialValue != null) {
1055
+ let initialValueStr;
1056
+ if (typeof definition.initialValue === "number") initialValueStr = String(definition.initialValue);
1057
+ else initialValueStr = parseStyle(definition.initialValue).output;
1058
+ parts.push(`initial-value: ${initialValueStr};`);
1059
+ }
1060
+ return `@property ${cssName} { ${parts.join(" ").trim()} }`;
1061
+ }
1062
+ //#endregion
1063
+ //#region src/ssr/format-keyframes.ts
1064
+ /**
1065
+ * Convert keyframes steps to a CSS string.
1066
+ * Replicates SheetManager.stepsToCSS() without the class instance.
1067
+ */
1068
+ function stepsToCSS(steps) {
1069
+ const rules = [];
1070
+ for (const [key, value] of Object.entries(steps)) {
1071
+ if (typeof value === "string") {
1072
+ rules.push(`${key} { ${value.trim()} }`);
1073
+ continue;
1074
+ }
1075
+ const styleMap = value || {};
1076
+ const styleNames = Object.keys(styleMap).sort();
1077
+ const handlerQueue = [];
1078
+ const seenHandlers = /* @__PURE__ */ new Set();
1079
+ styleNames.forEach((styleName) => {
1080
+ let handlers = STYLE_HANDLER_MAP[styleName];
1081
+ if (!handlers) handlers = STYLE_HANDLER_MAP[styleName] = [createStyle(styleName)];
1082
+ handlers.forEach((handler) => {
1083
+ if (!seenHandlers.has(handler)) {
1084
+ seenHandlers.add(handler);
1085
+ handlerQueue.push(handler);
1086
+ }
1087
+ });
1088
+ });
1089
+ const declarationPairs = [];
1090
+ handlerQueue.forEach((handler) => {
1091
+ const result = handler(handler.__lookupStyles.reduce((acc, name) => {
1092
+ const v = styleMap[name];
1093
+ if (v !== void 0) acc[name] = v;
1094
+ return acc;
1095
+ }, {}));
1096
+ if (!result) return;
1097
+ (Array.isArray(result) ? result : [result]).forEach((cssMap) => {
1098
+ if (!cssMap || typeof cssMap !== "object") return;
1099
+ const { $: _$, ...props } = cssMap;
1100
+ Object.entries(props).forEach(([prop, val]) => {
1101
+ if (val == null || val === "") return;
1102
+ if (Array.isArray(val)) val.forEach((v) => {
1103
+ if (v != null && v !== "") declarationPairs.push({
1104
+ prop,
1105
+ value: String(v)
1106
+ });
1107
+ });
1108
+ else declarationPairs.push({
1109
+ prop,
1110
+ value: String(val)
1111
+ });
1112
+ });
1113
+ });
1114
+ });
1115
+ const declarations = declarationPairs.map((d) => `${d.prop}: ${d.value}`).join("; ");
1116
+ rules.push(`${key} { ${declarations.trim()} }`);
1117
+ }
1118
+ return rules.join(" ");
1119
+ }
1120
+ /**
1121
+ * Format a @keyframes rule as a CSS string.
1122
+ */
1123
+ function formatKeyframesCSS(name, steps) {
1124
+ return `@keyframes ${name} { ${stepsToCSS(steps)} }`;
1125
+ }
1126
+ //#endregion
1127
+ //#region src/ssr/format-rules.ts
1128
+ /**
1129
+ * Resolve selectors for a rule, applying className-based specificity doubling
1130
+ * and rootPrefix handling. Mirrors the logic in StyleInjector.inject().
1131
+ */
1132
+ function resolveSelector(rule, className) {
1133
+ let selector = rule.selector;
1134
+ if (rule.needsClassName) {
1135
+ const selectorParts = selector ? selector.split("|||") : [""];
1136
+ const classPrefix = `.${className}.${className}`;
1137
+ selector = selectorParts.map((part) => {
1138
+ const classSelector = part ? `${classPrefix}${part}` : classPrefix;
1139
+ if (rule.rootPrefix) return `${rule.rootPrefix} ${classSelector}`;
1140
+ return classSelector;
1141
+ }).join(", ");
1142
+ }
1143
+ return selector;
1144
+ }
1145
+ /**
1146
+ * Group rules by selector + at-rules + startingStyle and merge their declarations.
1147
+ * Mirrors the grouping logic in SheetManager.insertRule().
1148
+ */
1149
+ function groupRules(rules) {
1150
+ const groupMap = /* @__PURE__ */ new Map();
1151
+ const order = [];
1152
+ const atKey = (at) => at && at.length ? at.join("|") : "";
1153
+ for (const r of rules) {
1154
+ const key = `${atKey(r.atRules)}||${r.selector}||${r.startingStyle ? "1" : "0"}`;
1155
+ const existing = groupMap.get(key);
1156
+ if (existing) existing.declarations = existing.declarations ? `${existing.declarations} ${r.declarations}` : r.declarations;
1157
+ else {
1158
+ groupMap.set(key, {
1159
+ selector: r.selector,
1160
+ atRules: r.atRules,
1161
+ startingStyle: r.startingStyle,
1162
+ declarations: r.declarations
1163
+ });
1164
+ order.push(key);
1165
+ }
1166
+ }
1167
+ return order.map((key) => groupMap.get(key));
1168
+ }
1169
+ /**
1170
+ * Format an array of StyleResult rules into a CSS text string.
1171
+ *
1172
+ * Applies className-based specificity doubling (.cls.cls),
1173
+ * groups rules by selector + at-rules, and wraps with at-rule blocks.
1174
+ *
1175
+ * Produces the same CSS text as SheetManager.insertRule() would insert
1176
+ * into the DOM, but as a plain string suitable for SSR output.
1177
+ */
1178
+ function formatRules(rules, className) {
1179
+ if (rules.length === 0) return "";
1180
+ const grouped = groupRules(rules.map((rule) => ({
1181
+ selector: resolveSelector(rule, className),
1182
+ declarations: rule.declarations,
1183
+ atRules: rule.atRules,
1184
+ startingStyle: rule.startingStyle
1185
+ })));
1186
+ const cssRules = [];
1187
+ for (const rule of grouped) {
1188
+ const innerContent = rule.startingStyle ? `@starting-style { ${rule.declarations} }` : rule.declarations;
1189
+ const baseRule = `${rule.selector} { ${innerContent} }`;
1190
+ let fullRule = baseRule;
1191
+ if (rule.atRules && rule.atRules.length > 0) fullRule = rule.atRules.reduce((css, atRule) => `${atRule} { ${css} }`, baseRule);
1192
+ cssRules.push(fullRule);
1193
+ }
1194
+ return cssRules.join("\n");
1195
+ }
1196
+ //#endregion
1197
+ //#region src/ssr/format-global-rules.ts
1198
+ /**
1199
+ * Format an array of global StyleResult rules into a CSS text string.
1200
+ *
1201
+ * Rules already have their full selectors applied by renderStyles().
1202
+ * Handles rootPrefix prepending and at-rule wrapping.
1203
+ */
1204
+ function formatGlobalRules(rules) {
1205
+ if (rules.length === 0) return "";
1206
+ const cssRules = [];
1207
+ for (const rule of rules) {
1208
+ const baseRule = `${rule.rootPrefix ? `${rule.rootPrefix} ${rule.selector}` : rule.selector} { ${rule.declarations} }`;
1209
+ let fullRule = baseRule;
1210
+ if (rule.atRules && rule.atRules.length > 0) fullRule = rule.atRules.reduce((css, atRule) => `${atRule} { ${css} }`, baseRule);
1211
+ cssRules.push(fullRule);
1212
+ }
1213
+ return cssRules.join("\n");
1214
+ }
1215
+ //#endregion
1216
+ //#region src/ssr/artifacts.ts
1217
+ function createServerStyleArtifact(kind, key, css, order) {
1218
+ const content = `${kind}\0${key}\0${css}`;
1219
+ return {
1220
+ id: `${kind}:${hashString(content)}:${content.length.toString(36)}`,
1221
+ kind,
1222
+ css,
1223
+ order
1224
+ };
1225
+ }
1226
+ //#endregion
1227
+ export { validateNamePrefix as A, formatFunctionPrelude as B, DEFAULT_NAME_PREFIX as C, makeKeyframeName as D, makeCounterStyleName as E, normalizePropertyDefinition as F, registerLocalFunctionPolyfills as G, hasLocalFunctions as H, parsePropertyToken as I, resetFunctionPolyfills as K, DEFAULT_PROPERTIES as L, extractLocalProperties as M, getEffectiveDefinition as N, rscClassRegexGlobal as O, hasLocalProperties as P, extractLocalFunctions as R, PropertyTypeResolver as S, makeClassName as T, parseFunctionName as U, formatFunctionRule as V, registerFunctionPolyfill as W, extractLocalFontFace as _, formatPropertyCSS as a, formatFontFaceRule as b, filterUsedKeyframes as c, replaceAnimationNames as d, resolveKeyframesNames as f, hasLocalCounterStyle as g, formatCounterStyleRule as h, formatKeyframesCSS as i, hashString as j, tastyClassRegex as k, hasLocalKeyframes as l, formatCounterStyleDeclarations as m, formatGlobalRules as n, extractAnimationNamesFromStyles as o, extractLocalCounterStyle as p, splitFunctions as q, formatRules as r, extractLocalKeyframes as s, createServerStyleArtifact as t, mergeKeyframes as u, fontFaceContentHash as v, DEFAULT_ZERO_NAME_PREFIX as w, hasLocalFontFace as x, formatFontFaceDeclarations as y, formatFunctionDeclarations as z };
1228
+
1229
+ //# sourceMappingURL=css-definitions-Crstltrf.js.map