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

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