@tenphi/tasty 0.0.0-snapshot.01d8a9e

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 (332) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +635 -0
  3. package/dist/_virtual/_rolldown/runtime.js +7 -0
  4. package/dist/chunks/cacheKey.d.ts +1 -0
  5. package/dist/chunks/cacheKey.js +77 -0
  6. package/dist/chunks/cacheKey.js.map +1 -0
  7. package/dist/chunks/definitions.d.ts +37 -0
  8. package/dist/chunks/definitions.js +258 -0
  9. package/dist/chunks/definitions.js.map +1 -0
  10. package/dist/chunks/index.d.ts +1 -0
  11. package/dist/chunks/renderChunk.d.ts +1 -0
  12. package/dist/chunks/renderChunk.js +59 -0
  13. package/dist/chunks/renderChunk.js.map +1 -0
  14. package/dist/compute-styles.d.ts +31 -0
  15. package/dist/compute-styles.js +336 -0
  16. package/dist/compute-styles.js.map +1 -0
  17. package/dist/config.d.ts +407 -0
  18. package/dist/config.js +591 -0
  19. package/dist/config.js.map +1 -0
  20. package/dist/core/index.d.ts +34 -0
  21. package/dist/core/index.js +27 -0
  22. package/dist/counter-style/index.js +51 -0
  23. package/dist/counter-style/index.js.map +1 -0
  24. package/dist/debug.d.ts +89 -0
  25. package/dist/debug.js +453 -0
  26. package/dist/debug.js.map +1 -0
  27. package/dist/font-face/index.js +63 -0
  28. package/dist/font-face/index.js.map +1 -0
  29. package/dist/hooks/index.d.ts +7 -0
  30. package/dist/hooks/useCounterStyle.d.ts +36 -0
  31. package/dist/hooks/useCounterStyle.js +65 -0
  32. package/dist/hooks/useCounterStyle.js.map +1 -0
  33. package/dist/hooks/useFontFace.d.ts +45 -0
  34. package/dist/hooks/useFontFace.js +66 -0
  35. package/dist/hooks/useFontFace.js.map +1 -0
  36. package/dist/hooks/useGlobalStyles.d.ts +46 -0
  37. package/dist/hooks/useGlobalStyles.js +88 -0
  38. package/dist/hooks/useGlobalStyles.js.map +1 -0
  39. package/dist/hooks/useKeyframes.d.ts +58 -0
  40. package/dist/hooks/useKeyframes.js +55 -0
  41. package/dist/hooks/useKeyframes.js.map +1 -0
  42. package/dist/hooks/useProperty.d.ts +81 -0
  43. package/dist/hooks/useProperty.js +96 -0
  44. package/dist/hooks/useProperty.js.map +1 -0
  45. package/dist/hooks/useRawCSS.d.ts +22 -0
  46. package/dist/hooks/useRawCSS.js +103 -0
  47. package/dist/hooks/useRawCSS.js.map +1 -0
  48. package/dist/hooks/useStyles.d.ts +40 -0
  49. package/dist/hooks/useStyles.js +31 -0
  50. package/dist/hooks/useStyles.js.map +1 -0
  51. package/dist/index.d.ts +51 -0
  52. package/dist/index.js +36 -0
  53. package/dist/injector/index.d.ts +182 -0
  54. package/dist/injector/index.js +185 -0
  55. package/dist/injector/index.js.map +1 -0
  56. package/dist/injector/injector.d.ts +198 -0
  57. package/dist/injector/injector.js +651 -0
  58. package/dist/injector/injector.js.map +1 -0
  59. package/dist/injector/sheet-manager.d.ts +132 -0
  60. package/dist/injector/sheet-manager.js +699 -0
  61. package/dist/injector/sheet-manager.js.map +1 -0
  62. package/dist/injector/types.d.ts +235 -0
  63. package/dist/keyframes/index.js +206 -0
  64. package/dist/keyframes/index.js.map +1 -0
  65. package/dist/parser/classify.js +319 -0
  66. package/dist/parser/classify.js.map +1 -0
  67. package/dist/parser/const.js +60 -0
  68. package/dist/parser/const.js.map +1 -0
  69. package/dist/parser/lru.js +109 -0
  70. package/dist/parser/lru.js.map +1 -0
  71. package/dist/parser/parser.d.ts +25 -0
  72. package/dist/parser/parser.js +115 -0
  73. package/dist/parser/parser.js.map +1 -0
  74. package/dist/parser/tokenizer.js +69 -0
  75. package/dist/parser/tokenizer.js.map +1 -0
  76. package/dist/parser/types.d.ts +51 -0
  77. package/dist/parser/types.js +46 -0
  78. package/dist/parser/types.js.map +1 -0
  79. package/dist/pipeline/conditions.d.ts +134 -0
  80. package/dist/pipeline/conditions.js +406 -0
  81. package/dist/pipeline/conditions.js.map +1 -0
  82. package/dist/pipeline/exclusive.js +283 -0
  83. package/dist/pipeline/exclusive.js.map +1 -0
  84. package/dist/pipeline/index.d.ts +55 -0
  85. package/dist/pipeline/index.js +708 -0
  86. package/dist/pipeline/index.js.map +1 -0
  87. package/dist/pipeline/materialize.js +1157 -0
  88. package/dist/pipeline/materialize.js.map +1 -0
  89. package/dist/pipeline/parseStateKey.d.ts +15 -0
  90. package/dist/pipeline/parseStateKey.js +446 -0
  91. package/dist/pipeline/parseStateKey.js.map +1 -0
  92. package/dist/pipeline/simplify.js +690 -0
  93. package/dist/pipeline/simplify.js.map +1 -0
  94. package/dist/pipeline/warnings.js +18 -0
  95. package/dist/pipeline/warnings.js.map +1 -0
  96. package/dist/plugins/index.d.ts +2 -0
  97. package/dist/plugins/okhsl-plugin.d.ts +35 -0
  98. package/dist/plugins/okhsl-plugin.js +97 -0
  99. package/dist/plugins/okhsl-plugin.js.map +1 -0
  100. package/dist/plugins/types.d.ts +87 -0
  101. package/dist/properties/index.js +222 -0
  102. package/dist/properties/index.js.map +1 -0
  103. package/dist/properties/property-type-resolver.d.ts +24 -0
  104. package/dist/properties/property-type-resolver.js +90 -0
  105. package/dist/properties/property-type-resolver.js.map +1 -0
  106. package/dist/rsc-cache.js +79 -0
  107. package/dist/rsc-cache.js.map +1 -0
  108. package/dist/ssr/astro-client.d.ts +1 -0
  109. package/dist/ssr/astro-client.js +19 -0
  110. package/dist/ssr/astro-client.js.map +1 -0
  111. package/dist/ssr/astro-middleware.d.ts +15 -0
  112. package/dist/ssr/astro-middleware.js +19 -0
  113. package/dist/ssr/astro-middleware.js.map +1 -0
  114. package/dist/ssr/astro.d.ts +108 -0
  115. package/dist/ssr/astro.js +149 -0
  116. package/dist/ssr/astro.js.map +1 -0
  117. package/dist/ssr/async-storage.d.ts +17 -0
  118. package/dist/ssr/async-storage.js +44 -0
  119. package/dist/ssr/async-storage.js.map +1 -0
  120. package/dist/ssr/collect-auto-properties.js +58 -0
  121. package/dist/ssr/collect-auto-properties.js.map +1 -0
  122. package/dist/ssr/collector.d.ts +94 -0
  123. package/dist/ssr/collector.js +233 -0
  124. package/dist/ssr/collector.js.map +1 -0
  125. package/dist/ssr/context.js +16 -0
  126. package/dist/ssr/context.js.map +1 -0
  127. package/dist/ssr/format-global-rules.js +22 -0
  128. package/dist/ssr/format-global-rules.js.map +1 -0
  129. package/dist/ssr/format-keyframes.js +69 -0
  130. package/dist/ssr/format-keyframes.js.map +1 -0
  131. package/dist/ssr/format-property.js +49 -0
  132. package/dist/ssr/format-property.js.map +1 -0
  133. package/dist/ssr/format-rules.js +73 -0
  134. package/dist/ssr/format-rules.js.map +1 -0
  135. package/dist/ssr/hydrate.d.ts +29 -0
  136. package/dist/ssr/hydrate.js +45 -0
  137. package/dist/ssr/hydrate.js.map +1 -0
  138. package/dist/ssr/index.d.ts +4 -0
  139. package/dist/ssr/index.js +10 -0
  140. package/dist/ssr/index.js.map +1 -0
  141. package/dist/ssr/next.d.ts +46 -0
  142. package/dist/ssr/next.js +75 -0
  143. package/dist/ssr/next.js.map +1 -0
  144. package/dist/ssr/ssr-collector-ref.js +29 -0
  145. package/dist/ssr/ssr-collector-ref.js.map +1 -0
  146. package/dist/states/index.d.ts +49 -0
  147. package/dist/states/index.js +170 -0
  148. package/dist/states/index.js.map +1 -0
  149. package/dist/static/index.d.ts +5 -0
  150. package/dist/static/index.js +4 -0
  151. package/dist/static/inject.d.ts +5 -0
  152. package/dist/static/inject.js +17 -0
  153. package/dist/static/inject.js.map +1 -0
  154. package/dist/static/tastyStatic.d.ts +46 -0
  155. package/dist/static/tastyStatic.js +30 -0
  156. package/dist/static/tastyStatic.js.map +1 -0
  157. package/dist/static/types.d.ts +49 -0
  158. package/dist/static/types.js +24 -0
  159. package/dist/static/types.js.map +1 -0
  160. package/dist/styles/border.d.ts +25 -0
  161. package/dist/styles/border.js +120 -0
  162. package/dist/styles/border.js.map +1 -0
  163. package/dist/styles/color.d.ts +14 -0
  164. package/dist/styles/color.js +26 -0
  165. package/dist/styles/color.js.map +1 -0
  166. package/dist/styles/const.js +17 -0
  167. package/dist/styles/const.js.map +1 -0
  168. package/dist/styles/createStyle.js +79 -0
  169. package/dist/styles/createStyle.js.map +1 -0
  170. package/dist/styles/dimension.js +109 -0
  171. package/dist/styles/dimension.js.map +1 -0
  172. package/dist/styles/directional.js +133 -0
  173. package/dist/styles/directional.js.map +1 -0
  174. package/dist/styles/display.d.ts +30 -0
  175. package/dist/styles/display.js +73 -0
  176. package/dist/styles/display.js.map +1 -0
  177. package/dist/styles/fade.d.ts +15 -0
  178. package/dist/styles/fade.js +62 -0
  179. package/dist/styles/fade.js.map +1 -0
  180. package/dist/styles/fill.d.ts +42 -0
  181. package/dist/styles/fill.js +51 -0
  182. package/dist/styles/fill.js.map +1 -0
  183. package/dist/styles/flow.d.ts +16 -0
  184. package/dist/styles/flow.js +12 -0
  185. package/dist/styles/flow.js.map +1 -0
  186. package/dist/styles/gap.d.ts +31 -0
  187. package/dist/styles/gap.js +38 -0
  188. package/dist/styles/gap.js.map +1 -0
  189. package/dist/styles/height.d.ts +17 -0
  190. package/dist/styles/height.js +19 -0
  191. package/dist/styles/height.js.map +1 -0
  192. package/dist/styles/index.d.ts +1 -0
  193. package/dist/styles/index.js +8 -0
  194. package/dist/styles/index.js.map +1 -0
  195. package/dist/styles/inset.d.ts +24 -0
  196. package/dist/styles/inset.js +34 -0
  197. package/dist/styles/inset.js.map +1 -0
  198. package/dist/styles/list.d.ts +16 -0
  199. package/dist/styles/list.js +100 -0
  200. package/dist/styles/list.js.map +1 -0
  201. package/dist/styles/margin.d.ts +24 -0
  202. package/dist/styles/margin.js +32 -0
  203. package/dist/styles/margin.js.map +1 -0
  204. package/dist/styles/outline.d.ts +29 -0
  205. package/dist/styles/outline.js +55 -0
  206. package/dist/styles/outline.js.map +1 -0
  207. package/dist/styles/padding.d.ts +24 -0
  208. package/dist/styles/padding.js +32 -0
  209. package/dist/styles/padding.js.map +1 -0
  210. package/dist/styles/placement.d.ts +37 -0
  211. package/dist/styles/placement.js +74 -0
  212. package/dist/styles/placement.js.map +1 -0
  213. package/dist/styles/predefined.d.ts +71 -0
  214. package/dist/styles/predefined.js +237 -0
  215. package/dist/styles/predefined.js.map +1 -0
  216. package/dist/styles/preset.d.ts +52 -0
  217. package/dist/styles/preset.js +127 -0
  218. package/dist/styles/preset.js.map +1 -0
  219. package/dist/styles/radius.d.ts +12 -0
  220. package/dist/styles/radius.js +83 -0
  221. package/dist/styles/radius.js.map +1 -0
  222. package/dist/styles/scrollMargin.d.ts +24 -0
  223. package/dist/styles/scrollMargin.js +32 -0
  224. package/dist/styles/scrollMargin.js.map +1 -0
  225. package/dist/styles/scrollbar.d.ts +25 -0
  226. package/dist/styles/scrollbar.js +51 -0
  227. package/dist/styles/scrollbar.js.map +1 -0
  228. package/dist/styles/shadow.d.ts +14 -0
  229. package/dist/styles/shadow.js +25 -0
  230. package/dist/styles/shadow.js.map +1 -0
  231. package/dist/styles/shared.js +17 -0
  232. package/dist/styles/shared.js.map +1 -0
  233. package/dist/styles/transition.d.ts +14 -0
  234. package/dist/styles/transition.js +159 -0
  235. package/dist/styles/transition.js.map +1 -0
  236. package/dist/styles/types.d.ts +564 -0
  237. package/dist/styles/width.d.ts +17 -0
  238. package/dist/styles/width.js +19 -0
  239. package/dist/styles/width.js.map +1 -0
  240. package/dist/tasty.d.ts +134 -0
  241. package/dist/tasty.js +248 -0
  242. package/dist/tasty.js.map +1 -0
  243. package/dist/types.d.ts +184 -0
  244. package/dist/utils/cache-wrapper.js +21 -0
  245. package/dist/utils/cache-wrapper.js.map +1 -0
  246. package/dist/utils/case-converter.js +8 -0
  247. package/dist/utils/case-converter.js.map +1 -0
  248. package/dist/utils/color-math.d.ts +46 -0
  249. package/dist/utils/color-math.js +749 -0
  250. package/dist/utils/color-math.js.map +1 -0
  251. package/dist/utils/color-space.d.ts +5 -0
  252. package/dist/utils/color-space.js +228 -0
  253. package/dist/utils/color-space.js.map +1 -0
  254. package/dist/utils/colors.d.ts +5 -0
  255. package/dist/utils/colors.js +10 -0
  256. package/dist/utils/colors.js.map +1 -0
  257. package/dist/utils/css-types.d.ts +7 -0
  258. package/dist/utils/deps-equal.js +15 -0
  259. package/dist/utils/deps-equal.js.map +1 -0
  260. package/dist/utils/dotize.d.ts +26 -0
  261. package/dist/utils/dotize.js +122 -0
  262. package/dist/utils/dotize.js.map +1 -0
  263. package/dist/utils/filter-base-props.d.ts +15 -0
  264. package/dist/utils/filter-base-props.js +45 -0
  265. package/dist/utils/filter-base-props.js.map +1 -0
  266. package/dist/utils/get-display-name.d.ts +7 -0
  267. package/dist/utils/get-display-name.js +10 -0
  268. package/dist/utils/get-display-name.js.map +1 -0
  269. package/dist/utils/has-keys.js +13 -0
  270. package/dist/utils/has-keys.js.map +1 -0
  271. package/dist/utils/hash.js +14 -0
  272. package/dist/utils/hash.js.map +1 -0
  273. package/dist/utils/is-dev-env.js +19 -0
  274. package/dist/utils/is-dev-env.js.map +1 -0
  275. package/dist/utils/is-valid-element-type.js +15 -0
  276. package/dist/utils/is-valid-element-type.js.map +1 -0
  277. package/dist/utils/merge-styles.d.ts +7 -0
  278. package/dist/utils/merge-styles.js +145 -0
  279. package/dist/utils/merge-styles.js.map +1 -0
  280. package/dist/utils/mod-attrs.d.ts +6 -0
  281. package/dist/utils/mod-attrs.js +20 -0
  282. package/dist/utils/mod-attrs.js.map +1 -0
  283. package/dist/utils/process-tokens.d.ts +17 -0
  284. package/dist/utils/process-tokens.js +83 -0
  285. package/dist/utils/process-tokens.js.map +1 -0
  286. package/dist/utils/resolve-recipes.d.ts +17 -0
  287. package/dist/utils/resolve-recipes.js +146 -0
  288. package/dist/utils/resolve-recipes.js.map +1 -0
  289. package/dist/utils/selector-transform.js +32 -0
  290. package/dist/utils/selector-transform.js.map +1 -0
  291. package/dist/utils/string.js +8 -0
  292. package/dist/utils/string.js.map +1 -0
  293. package/dist/utils/styles.d.ts +99 -0
  294. package/dist/utils/styles.js +220 -0
  295. package/dist/utils/styles.js.map +1 -0
  296. package/dist/utils/typography.d.ts +58 -0
  297. package/dist/utils/typography.js +51 -0
  298. package/dist/utils/typography.js.map +1 -0
  299. package/dist/utils/warnings.d.ts +16 -0
  300. package/dist/utils/warnings.js +16 -0
  301. package/dist/utils/warnings.js.map +1 -0
  302. package/dist/zero/babel.d.ts +81 -0
  303. package/dist/zero/babel.js +462 -0
  304. package/dist/zero/babel.js.map +1 -0
  305. package/dist/zero/css-writer.d.ts +45 -0
  306. package/dist/zero/css-writer.js +73 -0
  307. package/dist/zero/css-writer.js.map +1 -0
  308. package/dist/zero/extractor.d.ts +24 -0
  309. package/dist/zero/extractor.js +266 -0
  310. package/dist/zero/extractor.js.map +1 -0
  311. package/dist/zero/index.d.ts +3 -0
  312. package/dist/zero/index.js +3 -0
  313. package/dist/zero/next.d.ts +86 -0
  314. package/dist/zero/next.js +143 -0
  315. package/dist/zero/next.js.map +1 -0
  316. package/docs/PIPELINE.md +519 -0
  317. package/docs/README.md +31 -0
  318. package/docs/adoption.md +298 -0
  319. package/docs/comparison.md +419 -0
  320. package/docs/configuration.md +394 -0
  321. package/docs/debug.md +318 -0
  322. package/docs/design-system.md +436 -0
  323. package/docs/dsl.md +688 -0
  324. package/docs/getting-started.md +217 -0
  325. package/docs/injector.md +544 -0
  326. package/docs/methodology.md +616 -0
  327. package/docs/react-api.md +557 -0
  328. package/docs/ssr.md +442 -0
  329. package/docs/styles.md +596 -0
  330. package/docs/tasty-static.md +532 -0
  331. package/package.json +221 -0
  332. package/tasty.config.ts +14 -0
@@ -0,0 +1,24 @@
1
+ //#region src/properties/property-type-resolver.d.ts
2
+ /**
3
+ * PropertyTypeResolver
4
+ *
5
+ * Automatically infers CSS @property types from custom property values.
6
+ * Supports deferred resolution for var() reference chains of arbitrary depth.
7
+ */
8
+ declare class PropertyTypeResolver {
9
+ /** propName → the prop it depends on */
10
+ private pendingDeps;
11
+ /** propName → list of props waiting on it */
12
+ private reverseDeps;
13
+ /**
14
+ * Scan CSS declarations and auto-register @property for custom properties
15
+ * whose types can be inferred from their values.
16
+ */
17
+ scanDeclarations(declarations: string, isPropertyDefined: (name: string) => boolean, registerProperty: (name: string, syntax: string, initialValue: string) => void): void;
18
+ private addDependency;
19
+ private resolve;
20
+ private isComplexValue;
21
+ }
22
+ //#endregion
23
+ export { PropertyTypeResolver };
24
+ //# sourceMappingURL=property-type-resolver.d.ts.map
@@ -0,0 +1,90 @@
1
+ import { inferSyntaxFromValue } from "./index.js";
2
+ //#region src/properties/property-type-resolver.ts
3
+ /**
4
+ * PropertyTypeResolver
5
+ *
6
+ * Automatically infers CSS @property types from custom property values.
7
+ * Supports deferred resolution for var() reference chains of arbitrary depth.
8
+ */
9
+ const CUSTOM_PROP_DECL = /^\s*(--[a-z0-9_-]+)\s*:\s*(.+?)\s*$/i;
10
+ const SINGLE_VAR_REF = /^var\((--[a-z0-9_-]+)\)$/i;
11
+ var PropertyTypeResolver = class {
12
+ /** propName → the prop it depends on */
13
+ pendingDeps = /* @__PURE__ */ new Map();
14
+ /** propName → list of props waiting on it */
15
+ reverseDeps = /* @__PURE__ */ new Map();
16
+ /**
17
+ * Scan CSS declarations and auto-register @property for custom properties
18
+ * whose types can be inferred from their values.
19
+ */
20
+ scanDeclarations(declarations, isPropertyDefined, registerProperty) {
21
+ if (!declarations.includes("--")) return;
22
+ const parts = declarations.split(/;+/);
23
+ for (const part of parts) {
24
+ if (!part.trim()) continue;
25
+ const match = CUSTOM_PROP_DECL.exec(part);
26
+ if (!match) continue;
27
+ const propName = match[1];
28
+ const value = match[2].trim();
29
+ if (isPropertyDefined(propName)) continue;
30
+ if (propName.endsWith("-color")) {
31
+ registerProperty(propName, "<color>", "transparent");
32
+ continue;
33
+ }
34
+ if (propName.endsWith("-line-height")) {
35
+ registerProperty(propName, "<number> | <length-percentage>", "0");
36
+ continue;
37
+ }
38
+ if (propName.endsWith("-opacity")) {
39
+ registerProperty(propName, "<number> | <percentage>", "0");
40
+ continue;
41
+ }
42
+ const varMatch = SINGLE_VAR_REF.exec(value);
43
+ if (varMatch) {
44
+ const depName = varMatch[1];
45
+ this.addDependency(propName, depName);
46
+ continue;
47
+ }
48
+ if (this.isComplexValue(value)) continue;
49
+ const inferred = inferSyntaxFromValue(value);
50
+ if (!inferred) continue;
51
+ this.resolve(propName, inferred.syntax, inferred.initialValue, isPropertyDefined, registerProperty);
52
+ }
53
+ }
54
+ addDependency(propName, depName) {
55
+ if (propName === depName) return;
56
+ this.pendingDeps.set(propName, depName);
57
+ let dependents = this.reverseDeps.get(depName);
58
+ if (!dependents) {
59
+ dependents = [];
60
+ this.reverseDeps.set(depName, dependents);
61
+ }
62
+ if (!dependents.includes(propName)) dependents.push(propName);
63
+ }
64
+ resolve(propName, syntax, initialValue, isPropertyDefined, registerProperty, resolving) {
65
+ if (!resolving) resolving = /* @__PURE__ */ new Set();
66
+ if (resolving.has(propName)) return;
67
+ resolving.add(propName);
68
+ if (!isPropertyDefined(propName)) registerProperty(propName, syntax, initialValue);
69
+ const dependents = this.reverseDeps.get(propName);
70
+ if (dependents) {
71
+ this.reverseDeps.delete(propName);
72
+ for (const dependent of dependents) {
73
+ this.pendingDeps.delete(dependent);
74
+ if (isPropertyDefined(dependent)) continue;
75
+ this.resolve(dependent, syntax, initialValue, isPropertyDefined, registerProperty, resolving);
76
+ }
77
+ }
78
+ }
79
+ isComplexValue(value) {
80
+ if (value.includes("calc(")) return true;
81
+ const firstVar = value.indexOf("var(");
82
+ if (firstVar === -1) return false;
83
+ if (value.indexOf("var(", firstVar + 4) !== -1) return true;
84
+ return !SINGLE_VAR_REF.test(value);
85
+ }
86
+ };
87
+ //#endregion
88
+ export { PropertyTypeResolver };
89
+
90
+ //# sourceMappingURL=property-type-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"property-type-resolver.js","names":[],"sources":["../../src/properties/property-type-resolver.ts"],"sourcesContent":["/**\n * PropertyTypeResolver\n *\n * Automatically infers CSS @property types from custom property values.\n * Supports deferred resolution for var() reference chains of arbitrary depth.\n */\n\nimport { inferSyntaxFromValue } from './index';\n\nconst CUSTOM_PROP_DECL = /^\\s*(--[a-z0-9_-]+)\\s*:\\s*(.+?)\\s*$/i;\nconst SINGLE_VAR_REF = /^var\\((--[a-z0-9_-]+)\\)$/i;\n\nexport class PropertyTypeResolver {\n /** propName → the prop it depends on */\n private pendingDeps = new Map<string, string>();\n /** propName → list of props waiting on it */\n private reverseDeps = new Map<string, string[]>();\n\n /**\n * Scan CSS declarations and auto-register @property for custom properties\n * whose types can be inferred from their values.\n */\n scanDeclarations(\n declarations: string,\n isPropertyDefined: (name: string) => boolean,\n registerProperty: (\n name: string,\n syntax: string,\n initialValue: string,\n ) => void,\n ): void {\n if (!declarations.includes('--')) return;\n\n const parts = declarations.split(/;+/);\n\n for (const part of parts) {\n if (!part.trim()) continue;\n\n const match = CUSTOM_PROP_DECL.exec(part);\n if (!match) continue;\n\n const propName = match[1];\n const value = match[2].trim();\n\n if (isPropertyDefined(propName)) continue;\n\n // Name-based: --*-color properties are always <color> (from #name tokens)\n if (propName.endsWith('-color')) {\n registerProperty(propName, '<color>', 'transparent');\n continue;\n }\n\n // Name-based: --*-line-height accepts numbers, lengths, and percentages\n if (propName.endsWith('-line-height')) {\n registerProperty(propName, '<number> | <length-percentage>', '0');\n continue;\n }\n\n // Name-based: --*-opacity accepts numbers and percentages\n if (propName.endsWith('-opacity')) {\n registerProperty(propName, '<number> | <percentage>', '0');\n continue;\n }\n\n // Single var() reference → record dependency for deferred resolution\n const varMatch = SINGLE_VAR_REF.exec(value);\n if (varMatch) {\n const depName = varMatch[1];\n this.addDependency(propName, depName);\n continue;\n }\n\n // Skip complex expressions (calc, multiple var, etc.)\n if (this.isComplexValue(value)) continue;\n\n const inferred = inferSyntaxFromValue(value);\n if (!inferred) continue;\n\n this.resolve(\n propName,\n inferred.syntax,\n inferred.initialValue,\n isPropertyDefined,\n registerProperty,\n );\n }\n }\n\n private addDependency(propName: string, depName: string): void {\n if (propName === depName) return;\n\n this.pendingDeps.set(propName, depName);\n\n let dependents = this.reverseDeps.get(depName);\n if (!dependents) {\n dependents = [];\n this.reverseDeps.set(depName, dependents);\n }\n if (!dependents.includes(propName)) {\n dependents.push(propName);\n }\n }\n\n private resolve(\n propName: string,\n syntax: string,\n initialValue: string,\n isPropertyDefined: (name: string) => boolean,\n registerProperty: (\n name: string,\n syntax: string,\n initialValue: string,\n ) => void,\n resolving?: Set<string>,\n ): void {\n if (!resolving) resolving = new Set();\n if (resolving.has(propName)) return;\n resolving.add(propName);\n\n if (!isPropertyDefined(propName)) {\n registerProperty(propName, syntax, initialValue);\n }\n\n const dependents = this.reverseDeps.get(propName);\n if (dependents) {\n this.reverseDeps.delete(propName);\n\n for (const dependent of dependents) {\n this.pendingDeps.delete(dependent);\n\n if (isPropertyDefined(dependent)) continue;\n\n this.resolve(\n dependent,\n syntax,\n initialValue,\n isPropertyDefined,\n registerProperty,\n resolving,\n );\n }\n }\n }\n\n private isComplexValue(value: string): boolean {\n if (value.includes('calc(')) return true;\n const firstVar = value.indexOf('var(');\n if (firstVar === -1) return false;\n if (value.indexOf('var(', firstVar + 4) !== -1) return true;\n return !SINGLE_VAR_REF.test(value);\n }\n}\n"],"mappings":";;;;;;;;AASA,MAAM,mBAAmB;AACzB,MAAM,iBAAiB;AAEvB,IAAa,uBAAb,MAAkC;;CAEhC,8BAAsB,IAAI,KAAqB;;CAE/C,8BAAsB,IAAI,KAAuB;;;;;CAMjD,iBACE,cACA,mBACA,kBAKM;AACN,MAAI,CAAC,aAAa,SAAS,KAAK,CAAE;EAElC,MAAM,QAAQ,aAAa,MAAM,KAAK;AAEtC,OAAK,MAAM,QAAQ,OAAO;AACxB,OAAI,CAAC,KAAK,MAAM,CAAE;GAElB,MAAM,QAAQ,iBAAiB,KAAK,KAAK;AACzC,OAAI,CAAC,MAAO;GAEZ,MAAM,WAAW,MAAM;GACvB,MAAM,QAAQ,MAAM,GAAG,MAAM;AAE7B,OAAI,kBAAkB,SAAS,CAAE;AAGjC,OAAI,SAAS,SAAS,SAAS,EAAE;AAC/B,qBAAiB,UAAU,WAAW,cAAc;AACpD;;AAIF,OAAI,SAAS,SAAS,eAAe,EAAE;AACrC,qBAAiB,UAAU,kCAAkC,IAAI;AACjE;;AAIF,OAAI,SAAS,SAAS,WAAW,EAAE;AACjC,qBAAiB,UAAU,2BAA2B,IAAI;AAC1D;;GAIF,MAAM,WAAW,eAAe,KAAK,MAAM;AAC3C,OAAI,UAAU;IACZ,MAAM,UAAU,SAAS;AACzB,SAAK,cAAc,UAAU,QAAQ;AACrC;;AAIF,OAAI,KAAK,eAAe,MAAM,CAAE;GAEhC,MAAM,WAAW,qBAAqB,MAAM;AAC5C,OAAI,CAAC,SAAU;AAEf,QAAK,QACH,UACA,SAAS,QACT,SAAS,cACT,mBACA,iBACD;;;CAIL,cAAsB,UAAkB,SAAuB;AAC7D,MAAI,aAAa,QAAS;AAE1B,OAAK,YAAY,IAAI,UAAU,QAAQ;EAEvC,IAAI,aAAa,KAAK,YAAY,IAAI,QAAQ;AAC9C,MAAI,CAAC,YAAY;AACf,gBAAa,EAAE;AACf,QAAK,YAAY,IAAI,SAAS,WAAW;;AAE3C,MAAI,CAAC,WAAW,SAAS,SAAS,CAChC,YAAW,KAAK,SAAS;;CAI7B,QACE,UACA,QACA,cACA,mBACA,kBAKA,WACM;AACN,MAAI,CAAC,UAAW,6BAAY,IAAI,KAAK;AACrC,MAAI,UAAU,IAAI,SAAS,CAAE;AAC7B,YAAU,IAAI,SAAS;AAEvB,MAAI,CAAC,kBAAkB,SAAS,CAC9B,kBAAiB,UAAU,QAAQ,aAAa;EAGlD,MAAM,aAAa,KAAK,YAAY,IAAI,SAAS;AACjD,MAAI,YAAY;AACd,QAAK,YAAY,OAAO,SAAS;AAEjC,QAAK,MAAM,aAAa,YAAY;AAClC,SAAK,YAAY,OAAO,UAAU;AAElC,QAAI,kBAAkB,UAAU,CAAE;AAElC,SAAK,QACH,WACA,QACA,cACA,mBACA,kBACA,UACD;;;;CAKP,eAAuB,OAAwB;AAC7C,MAAI,MAAM,SAAS,QAAQ,CAAE,QAAO;EACpC,MAAM,WAAW,MAAM,QAAQ,OAAO;AACtC,MAAI,aAAa,GAAI,QAAO;AAC5B,MAAI,MAAM,QAAQ,QAAQ,WAAW,EAAE,KAAK,GAAI,QAAO;AACvD,SAAO,CAAC,eAAe,KAAK,MAAM"}
@@ -0,0 +1,79 @@
1
+ import { hashString } from "./utils/hash.js";
2
+ import { getRegisteredSSRCollector } from "./ssr/ssr-collector-ref.js";
3
+ import { cache } from "react";
4
+ //#region src/rsc-cache.ts
5
+ /**
6
+ * Shared RSC (React Server Components) inline style cache.
7
+ *
8
+ * Uses React.cache for per-request memoization in Server Components.
9
+ * Both computeStyles() and standalone style functions (useGlobalStyles,
10
+ * useRawCSS, useKeyframes, useProperty, useFontFace, useCounterStyle)
11
+ * share this cache so that CSS accumulated by standalone functions is
12
+ * flushed into inline <style> tags by the next tasty() component.
13
+ */
14
+ /**
15
+ * Per-request RSC style cache using React.cache.
16
+ * React.cache provides per-request memoization in Server Components,
17
+ * so each request gets its own isolated cache.
18
+ */
19
+ const getRSCCache = cache(() => ({
20
+ cacheKeyToClassName: /* @__PURE__ */ new Map(),
21
+ emittedKeys: /* @__PURE__ */ new Set(),
22
+ internalsEmitted: false,
23
+ pendingCSS: [],
24
+ generatedNames: /* @__PURE__ */ new Map()
25
+ }));
26
+ function rscAllocateClassName(rscCache, cacheKey) {
27
+ const existing = rscCache.cacheKeyToClassName.get(cacheKey);
28
+ if (existing) return {
29
+ className: existing,
30
+ isNew: false
31
+ };
32
+ const className = `t${hashString(cacheKey)}`;
33
+ rscCache.cacheKeyToClassName.set(cacheKey, className);
34
+ return {
35
+ className,
36
+ isNew: true
37
+ };
38
+ }
39
+ /**
40
+ * Flush any pending CSS accumulated by standalone functions.
41
+ * Returns the CSS string and clears the buffer.
42
+ */
43
+ function flushPendingCSS(rscCache) {
44
+ if (rscCache.pendingCSS.length === 0) return "";
45
+ const css = rscCache.pendingCSS.join("\n");
46
+ rscCache.pendingCSS.length = 0;
47
+ return css;
48
+ }
49
+ /**
50
+ * Push CSS into the RSC pending buffer with dedup via emittedKeys.
51
+ * Returns true if the CSS was added, false if it was already emitted.
52
+ */
53
+ function pushRSCCSS(rscCache, key, css) {
54
+ if (rscCache.emittedKeys.has(key)) return false;
55
+ rscCache.emittedKeys.add(key);
56
+ rscCache.pendingCSS.push(css);
57
+ return true;
58
+ }
59
+ /**
60
+ * Determine the current style injection target.
61
+ * Centralizes the three-way detection (SSR collector / RSC cache / client DOM)
62
+ * used by all style functions.
63
+ */
64
+ function getStyleTarget() {
65
+ const collector = getRegisteredSSRCollector();
66
+ if (collector) return {
67
+ mode: "ssr",
68
+ collector
69
+ };
70
+ if (typeof document === "undefined") return {
71
+ mode: "rsc",
72
+ cache: getRSCCache()
73
+ };
74
+ return { mode: "client" };
75
+ }
76
+ //#endregion
77
+ export { flushPendingCSS, getRSCCache, getStyleTarget, pushRSCCSS, rscAllocateClassName };
78
+
79
+ //# sourceMappingURL=rsc-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rsc-cache.js","names":[],"sources":["../src/rsc-cache.ts"],"sourcesContent":["/**\n * Shared RSC (React Server Components) inline style cache.\n *\n * Uses React.cache for per-request memoization in Server Components.\n * Both computeStyles() and standalone style functions (useGlobalStyles,\n * useRawCSS, useKeyframes, useProperty, useFontFace, useCounterStyle)\n * share this cache so that CSS accumulated by standalone functions is\n * flushed into inline <style> tags by the next tasty() component.\n */\n\nimport { cache } from 'react';\n\nimport type { ServerStyleCollector } from './ssr/collector';\nimport { getRegisteredSSRCollector } from './ssr/ssr-collector-ref';\nimport { hashString } from './utils/hash';\n\nexport interface RSCStyleCache {\n cacheKeyToClassName: Map<string, string>;\n emittedKeys: Set<string>;\n internalsEmitted: boolean;\n pendingCSS: string[];\n /** Maps dedup key -> generated name for keyframes and counter-styles in RSC mode. */\n generatedNames: Map<string, string>;\n}\n\n/**\n * Per-request RSC style cache using React.cache.\n * React.cache provides per-request memoization in Server Components,\n * so each request gets its own isolated cache.\n */\nexport const getRSCCache = cache(\n (): RSCStyleCache => ({\n cacheKeyToClassName: new Map(),\n emittedKeys: new Set(),\n internalsEmitted: false,\n pendingCSS: [],\n generatedNames: new Map(),\n }),\n);\n\nexport function rscAllocateClassName(\n rscCache: RSCStyleCache,\n cacheKey: string,\n): { className: string; isNew: boolean } {\n const existing = rscCache.cacheKeyToClassName.get(cacheKey);\n if (existing) return { className: existing, isNew: false };\n\n // Content-hash ensures stable names across all environments (RSC, SSR, client),\n // enabling cross-environment dedup and preventing class collisions.\n const className = `t${hashString(cacheKey)}`;\n rscCache.cacheKeyToClassName.set(cacheKey, className);\n return { className, isNew: true };\n}\n\n/**\n * Flush any pending CSS accumulated by standalone functions.\n * Returns the CSS string and clears the buffer.\n */\nexport function flushPendingCSS(rscCache: RSCStyleCache): string {\n if (rscCache.pendingCSS.length === 0) return '';\n const css = rscCache.pendingCSS.join('\\n');\n rscCache.pendingCSS.length = 0;\n return css;\n}\n\n/**\n * Push CSS into the RSC pending buffer with dedup via emittedKeys.\n * Returns true if the CSS was added, false if it was already emitted.\n */\nexport function pushRSCCSS(\n rscCache: RSCStyleCache,\n key: string,\n css: string,\n): boolean {\n if (rscCache.emittedKeys.has(key)) return false;\n rscCache.emittedKeys.add(key);\n rscCache.pendingCSS.push(css);\n return true;\n}\n\nexport type StyleTarget =\n | { mode: 'ssr'; collector: ServerStyleCollector }\n | { mode: 'rsc'; cache: RSCStyleCache }\n | { mode: 'client' };\n\n/**\n * Determine the current style injection target.\n * Centralizes the three-way detection (SSR collector / RSC cache / client DOM)\n * used by all style functions.\n */\nexport function getStyleTarget(): StyleTarget {\n const collector = getRegisteredSSRCollector();\n if (collector) return { mode: 'ssr', collector };\n if (typeof document === 'undefined')\n return { mode: 'rsc', cache: getRSCCache() };\n return { mode: 'client' };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA8BA,MAAa,cAAc,aACH;CACpB,qCAAqB,IAAI,KAAK;CAC9B,6BAAa,IAAI,KAAK;CACtB,kBAAkB;CAClB,YAAY,EAAE;CACd,gCAAgB,IAAI,KAAK;CAC1B,EACF;AAED,SAAgB,qBACd,UACA,UACuC;CACvC,MAAM,WAAW,SAAS,oBAAoB,IAAI,SAAS;AAC3D,KAAI,SAAU,QAAO;EAAE,WAAW;EAAU,OAAO;EAAO;CAI1D,MAAM,YAAY,IAAI,WAAW,SAAS;AAC1C,UAAS,oBAAoB,IAAI,UAAU,UAAU;AACrD,QAAO;EAAE;EAAW,OAAO;EAAM;;;;;;AAOnC,SAAgB,gBAAgB,UAAiC;AAC/D,KAAI,SAAS,WAAW,WAAW,EAAG,QAAO;CAC7C,MAAM,MAAM,SAAS,WAAW,KAAK,KAAK;AAC1C,UAAS,WAAW,SAAS;AAC7B,QAAO;;;;;;AAOT,SAAgB,WACd,UACA,KACA,KACS;AACT,KAAI,SAAS,YAAY,IAAI,IAAI,CAAE,QAAO;AAC1C,UAAS,YAAY,IAAI,IAAI;AAC7B,UAAS,WAAW,KAAK,IAAI;AAC7B,QAAO;;;;;;;AAaT,SAAgB,iBAA8B;CAC5C,MAAM,YAAY,2BAA2B;AAC7C,KAAI,UAAW,QAAO;EAAE,MAAM;EAAO;EAAW;AAChD,KAAI,OAAO,aAAa,YACtB,QAAO;EAAE,MAAM;EAAO,OAAO,aAAa;EAAE;AAC9C,QAAO,EAAE,MAAM,UAAU"}
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,19 @@
1
+ import { hydrateTastyClasses } from "./hydrate.js";
2
+ //#region src/ssr/astro-client.ts
3
+ /**
4
+ * Client-side cache hydration for Astro islands.
5
+ *
6
+ * Reads the class name list from `window.__TASTY__` (populated by
7
+ * inline scripts emitted during SSR) and pre-populates the injector
8
+ * so island hydration skips the style pipeline entirely.
9
+ *
10
+ * This module is browser-safe — it does NOT import node:async_hooks.
11
+ *
12
+ * Usage:
13
+ * - Automatically injected by tastyIntegration() via injectScript('before-hydration')
14
+ * - Can be imported manually: `import '@tenphi/tasty/ssr/astro-client'`
15
+ */
16
+ if (typeof window !== "undefined") hydrateTastyClasses();
17
+ //#endregion
18
+
19
+ //# sourceMappingURL=astro-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astro-client.js","names":[],"sources":["../../src/ssr/astro-client.ts"],"sourcesContent":["/**\n * Client-side cache hydration for Astro islands.\n *\n * Reads the class name list from `window.__TASTY__` (populated by\n * inline scripts emitted during SSR) and pre-populates the injector\n * so island hydration skips the style pipeline entirely.\n *\n * This module is browser-safe — it does NOT import node:async_hooks.\n *\n * Usage:\n * - Automatically injected by tastyIntegration() via injectScript('before-hydration')\n * - Can be imported manually: `import '@tenphi/tasty/ssr/astro-client'`\n */\n\nimport { hydrateTastyClasses } from './hydrate';\n\nif (typeof window !== 'undefined') {\n hydrateTastyClasses();\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,IAAI,OAAO,WAAW,YACpB,sBAAqB"}
@@ -0,0 +1,15 @@
1
+ //#region src/ssr/astro-middleware.d.ts
2
+ /**
3
+ * Astro middleware entrypoint for tastyIntegration().
4
+ *
5
+ * Referenced by the integration via addMiddleware(). Not intended
6
+ * as a public package export — use tastyMiddleware() directly if
7
+ * you need manual middleware composition.
8
+ *
9
+ * The transferCache setting is controlled by setMiddlewareTransferCache(),
10
+ * called by tastyIntegration() before middleware is loaded.
11
+ */
12
+ declare const onRequest: (_context: unknown, next: () => Promise<Response>) => Promise<Response>;
13
+ //#endregion
14
+ export { onRequest };
15
+ //# sourceMappingURL=astro-middleware.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { getMiddlewareTransferCache, tastyMiddleware } from "./astro.js";
2
+ //#region src/ssr/astro-middleware.ts
3
+ /**
4
+ * Astro middleware entrypoint for tastyIntegration().
5
+ *
6
+ * Referenced by the integration via addMiddleware(). Not intended
7
+ * as a public package export — use tastyMiddleware() directly if
8
+ * you need manual middleware composition.
9
+ *
10
+ * The transferCache setting is controlled by setMiddlewareTransferCache(),
11
+ * called by tastyIntegration() before middleware is loaded.
12
+ */
13
+ const onRequest = tastyMiddleware({ get transferCache() {
14
+ return getMiddlewareTransferCache();
15
+ } });
16
+ //#endregion
17
+ export { onRequest };
18
+
19
+ //# sourceMappingURL=astro-middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astro-middleware.js","names":[],"sources":["../../src/ssr/astro-middleware.ts"],"sourcesContent":["/**\n * Astro middleware entrypoint for tastyIntegration().\n *\n * Referenced by the integration via addMiddleware(). Not intended\n * as a public package export — use tastyMiddleware() directly if\n * you need manual middleware composition.\n *\n * The transferCache setting is controlled by setMiddlewareTransferCache(),\n * called by tastyIntegration() before middleware is loaded.\n */\n\nimport { getMiddlewareTransferCache, tastyMiddleware } from './astro';\n\nexport const onRequest = tastyMiddleware({\n get transferCache() {\n return getMiddlewareTransferCache();\n },\n});\n"],"mappings":";;;;;;;;;;;;AAaA,MAAa,YAAY,gBAAgB,EACvC,IAAI,gBAAgB;AAClB,QAAO,4BAA4B;GAEtC,CAAC"}
@@ -0,0 +1,108 @@
1
+ //#region src/ssr/astro.d.ts
2
+ /**
3
+ * Astro integration for Tasty SSR.
4
+ *
5
+ * Provides:
6
+ * - tastyIntegration() — Astro Integration API (recommended)
7
+ * - tastyMiddleware() — manual middleware for advanced composition
8
+ *
9
+ * Import from '@tenphi/tasty/ssr/astro'.
10
+ */
11
+ interface TastyMiddlewareOptions {
12
+ /**
13
+ * Whether to embed the class-list script for client hydration.
14
+ * Set to false to skip class transfer (e.g. for CSP restrictions).
15
+ * Without it, client components may re-inject CSS that already exists
16
+ * in server-rendered `<style>` tags. Default: true.
17
+ */
18
+ transferCache?: boolean;
19
+ }
20
+ /**
21
+ * Create an Astro middleware that collects Tasty styles during SSR.
22
+ *
23
+ * All React components rendered during the request will have their
24
+ * computeStyles() calls captured by the collector via AsyncLocalStorage.
25
+ * After rendering, the middleware injects the collected CSS into </head>.
26
+ *
27
+ * @example Manual middleware setup
28
+ * ```ts
29
+ * // src/middleware.ts
30
+ * import { tastyMiddleware } from '@tenphi/tasty/ssr/astro';
31
+ * export const onRequest = tastyMiddleware();
32
+ * ```
33
+ *
34
+ * @example Composing with other middleware
35
+ * ```ts
36
+ * // src/middleware.ts
37
+ * import { sequence } from 'astro:middleware';
38
+ * import { tastyMiddleware } from '@tenphi/tasty/ssr/astro';
39
+ *
40
+ * export const onRequest = sequence(
41
+ * tastyMiddleware(),
42
+ * myOtherMiddleware,
43
+ * );
44
+ * ```
45
+ */
46
+ declare function tastyMiddleware(options?: TastyMiddlewareOptions): (_context: unknown, next: () => Promise<Response>) => Promise<Response>;
47
+ /** @internal */
48
+ declare function setMiddlewareTransferCache(value: boolean): void;
49
+ /** @internal */
50
+ declare function getMiddlewareTransferCache(): boolean;
51
+ interface TastyIntegrationOptions {
52
+ /**
53
+ * Enable island hydration support.
54
+ *
55
+ * When `true` (default): injects a client hydration script via
56
+ * `injectScript('before-hydration')` and sets `transferCache: true`
57
+ * on the middleware. Islands skip the style pipeline during hydration.
58
+ *
59
+ * When `false`: no client JS is shipped and `transferCache` is set
60
+ * to `false`. Use this for fully static sites without `client:*`
61
+ * directives.
62
+ */
63
+ islands?: boolean;
64
+ }
65
+ /**
66
+ * Astro integration that automatically sets up Tasty SSR.
67
+ *
68
+ * Registers middleware for cross-component CSS deduplication and
69
+ * optionally injects a client hydration script for island support.
70
+ *
71
+ * @example Basic setup (with islands)
72
+ * ```ts
73
+ * // astro.config.mjs
74
+ * import { tastyIntegration } from '@tenphi/tasty/ssr/astro';
75
+ *
76
+ * export default defineConfig({
77
+ * integrations: [tastyIntegration()],
78
+ * });
79
+ * ```
80
+ *
81
+ * @example Static-only (no client JS)
82
+ * ```ts
83
+ * // astro.config.mjs
84
+ * import { tastyIntegration } from '@tenphi/tasty/ssr/astro';
85
+ *
86
+ * export default defineConfig({
87
+ * integrations: [tastyIntegration({ islands: false })],
88
+ * });
89
+ * ```
90
+ */
91
+ declare function tastyIntegration(options?: TastyIntegrationOptions): {
92
+ name: string;
93
+ hooks: {
94
+ 'astro:config:setup': ({
95
+ addMiddleware,
96
+ injectScript
97
+ }: {
98
+ addMiddleware: (middleware: {
99
+ entrypoint: string | URL;
100
+ order: "pre" | "post";
101
+ }) => void;
102
+ injectScript: (stage: string, content: string) => void;
103
+ }) => void;
104
+ };
105
+ };
106
+ //#endregion
107
+ export { TastyIntegrationOptions, TastyMiddlewareOptions, getMiddlewareTransferCache, setMiddlewareTransferCache, tastyIntegration, tastyMiddleware };
108
+ //# sourceMappingURL=astro.d.ts.map
@@ -0,0 +1,149 @@
1
+ import { getConfig } from "../config.js";
2
+ import { registerSSRCollectorGetterGlobal } from "./ssr-collector-ref.js";
3
+ import { ServerStyleCollector } from "./collector.js";
4
+ import { getSSRCollector, runWithCollector } from "./async-storage.js";
5
+ //#region src/ssr/astro.ts
6
+ /**
7
+ * Astro integration for Tasty SSR.
8
+ *
9
+ * Provides:
10
+ * - tastyIntegration() — Astro Integration API (recommended)
11
+ * - tastyMiddleware() — manual middleware for advanced composition
12
+ *
13
+ * Import from '@tenphi/tasty/ssr/astro'.
14
+ */
15
+ registerSSRCollectorGetterGlobal(getSSRCollector);
16
+ /**
17
+ * Create an Astro middleware that collects Tasty styles during SSR.
18
+ *
19
+ * All React components rendered during the request will have their
20
+ * computeStyles() calls captured by the collector via AsyncLocalStorage.
21
+ * After rendering, the middleware injects the collected CSS into </head>.
22
+ *
23
+ * @example Manual middleware setup
24
+ * ```ts
25
+ * // src/middleware.ts
26
+ * import { tastyMiddleware } from '@tenphi/tasty/ssr/astro';
27
+ * export const onRequest = tastyMiddleware();
28
+ * ```
29
+ *
30
+ * @example Composing with other middleware
31
+ * ```ts
32
+ * // src/middleware.ts
33
+ * import { sequence } from 'astro:middleware';
34
+ * import { tastyMiddleware } from '@tenphi/tasty/ssr/astro';
35
+ *
36
+ * export const onRequest = sequence(
37
+ * tastyMiddleware(),
38
+ * myOtherMiddleware,
39
+ * );
40
+ * ```
41
+ */
42
+ function tastyMiddleware(options) {
43
+ return async (_context, next) => {
44
+ const transferCache = options?.transferCache ?? true;
45
+ const collector = new ServerStyleCollector();
46
+ const rendered = await runWithCollector(collector, async () => {
47
+ const response = await next();
48
+ const body = response.body;
49
+ if (!body) return {
50
+ html: null,
51
+ status: response.status,
52
+ headers: response.headers
53
+ };
54
+ const reader = body.pipeThrough(new TextDecoderStream()).getReader();
55
+ const parts = [];
56
+ for (;;) {
57
+ const { done, value } = await reader.read();
58
+ if (done) break;
59
+ parts.push(value);
60
+ }
61
+ return {
62
+ html: parts.join(""),
63
+ status: response.status,
64
+ headers: response.headers
65
+ };
66
+ });
67
+ if (!rendered.html) return new Response(null, {
68
+ status: rendered.status,
69
+ headers: rendered.headers
70
+ });
71
+ let { html } = rendered;
72
+ const css = collector.getCSS();
73
+ if (!css) return new Response(html, {
74
+ status: rendered.status,
75
+ headers: rendered.headers
76
+ });
77
+ const nonce = getConfig().nonce;
78
+ const nonceAttr = nonce ? ` nonce="${nonce}"` : "";
79
+ const styleTag = `<style data-tasty-ssr${nonceAttr}>${css}</style>`;
80
+ let cacheTag = "";
81
+ if (transferCache) {
82
+ const classNames = collector.getRenderedClassNames();
83
+ if (classNames.length > 0) cacheTag = `<script${nonceAttr}>(window.__TASTY__=window.__TASTY__||[]).push(${classNames.map((n) => `"${n}"`).join(",")})<\/script>`;
84
+ }
85
+ const injection = styleTag + cacheTag;
86
+ const idx = html.indexOf("</head>");
87
+ if (idx !== -1) html = html.slice(0, idx) + injection + html.slice(idx);
88
+ else html = injection + html;
89
+ const headers = new Headers(rendered.headers);
90
+ headers.delete("content-length");
91
+ return new Response(html, {
92
+ status: rendered.status,
93
+ headers
94
+ });
95
+ };
96
+ }
97
+ let _middlewareTransferCache = true;
98
+ /** @internal */
99
+ function setMiddlewareTransferCache(value) {
100
+ _middlewareTransferCache = value;
101
+ }
102
+ /** @internal */
103
+ function getMiddlewareTransferCache() {
104
+ return _middlewareTransferCache;
105
+ }
106
+ /**
107
+ * Astro integration that automatically sets up Tasty SSR.
108
+ *
109
+ * Registers middleware for cross-component CSS deduplication and
110
+ * optionally injects a client hydration script for island support.
111
+ *
112
+ * @example Basic setup (with islands)
113
+ * ```ts
114
+ * // astro.config.mjs
115
+ * import { tastyIntegration } from '@tenphi/tasty/ssr/astro';
116
+ *
117
+ * export default defineConfig({
118
+ * integrations: [tastyIntegration()],
119
+ * });
120
+ * ```
121
+ *
122
+ * @example Static-only (no client JS)
123
+ * ```ts
124
+ * // astro.config.mjs
125
+ * import { tastyIntegration } from '@tenphi/tasty/ssr/astro';
126
+ *
127
+ * export default defineConfig({
128
+ * integrations: [tastyIntegration({ islands: false })],
129
+ * });
130
+ * ```
131
+ */
132
+ function tastyIntegration(options) {
133
+ const { islands = true } = options ?? {};
134
+ setMiddlewareTransferCache(islands);
135
+ return {
136
+ name: "@tenphi/tasty",
137
+ hooks: { "astro:config:setup": ({ addMiddleware, injectScript }) => {
138
+ addMiddleware({
139
+ entrypoint: new URL("./astro-middleware.js", import.meta.url),
140
+ order: "pre"
141
+ });
142
+ if (islands) injectScript("before-hydration", `import "@tenphi/tasty/ssr/astro-client";`);
143
+ } }
144
+ };
145
+ }
146
+ //#endregion
147
+ export { getMiddlewareTransferCache, setMiddlewareTransferCache, tastyIntegration, tastyMiddleware };
148
+
149
+ //# sourceMappingURL=astro.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astro.js","names":[],"sources":["../../src/ssr/astro.ts"],"sourcesContent":["/**\n * Astro integration for Tasty SSR.\n *\n * Provides:\n * - tastyIntegration() — Astro Integration API (recommended)\n * - tastyMiddleware() — manual middleware for advanced composition\n *\n * Import from '@tenphi/tasty/ssr/astro'.\n */\n\nimport { getConfig } from '../config';\nimport { getSSRCollector, runWithCollector } from './async-storage';\nimport { ServerStyleCollector } from './collector';\nimport { registerSSRCollectorGetterGlobal } from './ssr-collector-ref';\n\n// Wire up ALS-based collector discovery so computeStyles() can find\n// the collector set by tastyMiddleware's runWithCollector().\n// Uses globalThis so the getter is visible across Astro's separate\n// module graphs (middleware vs page components).\nregisterSSRCollectorGetterGlobal(getSSRCollector);\n\nexport interface TastyMiddlewareOptions {\n /**\n * Whether to embed the class-list script for client hydration.\n * Set to false to skip class transfer (e.g. for CSP restrictions).\n * Without it, client components may re-inject CSS that already exists\n * in server-rendered `<style>` tags. Default: true.\n */\n transferCache?: boolean;\n}\n\n/**\n * Create an Astro middleware that collects Tasty styles during SSR.\n *\n * All React components rendered during the request will have their\n * computeStyles() calls captured by the collector via AsyncLocalStorage.\n * After rendering, the middleware injects the collected CSS into </head>.\n *\n * @example Manual middleware setup\n * ```ts\n * // src/middleware.ts\n * import { tastyMiddleware } from '@tenphi/tasty/ssr/astro';\n * export const onRequest = tastyMiddleware();\n * ```\n *\n * @example Composing with other middleware\n * ```ts\n * // src/middleware.ts\n * import { sequence } from 'astro:middleware';\n * import { tastyMiddleware } from '@tenphi/tasty/ssr/astro';\n *\n * export const onRequest = sequence(\n * tastyMiddleware(),\n * myOtherMiddleware,\n * );\n * ```\n */\nexport function tastyMiddleware(options?: TastyMiddlewareOptions) {\n return async (\n _context: unknown,\n next: () => Promise<Response>,\n ): Promise<Response> => {\n const transferCache = options?.transferCache ?? true;\n const collector = new ServerStyleCollector();\n\n // Run the entire request — including body stream consumption — inside\n // the ALS context so that components rendering lazily during stream\n // reads can still find the collector via getSSRCollector().\n const rendered = await runWithCollector(collector, async () => {\n const response = await next();\n const body = response.body;\n if (!body) {\n return {\n html: null as string | null,\n status: response.status,\n headers: response.headers,\n };\n }\n\n const reader = body.pipeThrough(new TextDecoderStream()).getReader();\n const parts: string[] = [];\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n parts.push(value);\n }\n return {\n html: parts.join(''),\n status: response.status,\n headers: response.headers,\n };\n });\n\n if (!rendered.html) {\n return new Response(null, {\n status: rendered.status,\n headers: rendered.headers,\n });\n }\n\n let { html } = rendered;\n\n const css = collector.getCSS();\n if (!css) {\n return new Response(html, {\n status: rendered.status,\n headers: rendered.headers,\n });\n }\n\n const nonce = getConfig().nonce;\n const nonceAttr = nonce ? ` nonce=\"${nonce}\"` : '';\n const styleTag = `<style data-tasty-ssr${nonceAttr}>${css}</style>`;\n\n let cacheTag = '';\n if (transferCache) {\n const classNames = collector.getRenderedClassNames();\n if (classNames.length > 0) {\n const classListJSON = classNames.map((n) => `\"${n}\"`).join(',');\n cacheTag = `<script${nonceAttr}>(window.__TASTY__=window.__TASTY__||[]).push(${classListJSON})</script>`;\n }\n }\n\n const injection = styleTag + cacheTag;\n const idx = html.indexOf('</head>');\n if (idx !== -1) {\n html = html.slice(0, idx) + injection + html.slice(idx);\n } else {\n html = injection + html;\n }\n\n const headers = new Headers(rendered.headers);\n headers.delete('content-length');\n\n return new Response(html, {\n status: rendered.status,\n headers,\n });\n };\n}\n\n// ============================================================================\n// Module-level middleware config (set by tastyIntegration, read by\n// astro-middleware.ts via getter property)\n// ============================================================================\n\nlet _middlewareTransferCache = true;\n\n/** @internal */\nexport function setMiddlewareTransferCache(value: boolean): void {\n _middlewareTransferCache = value;\n}\n\n/** @internal */\nexport function getMiddlewareTransferCache(): boolean {\n return _middlewareTransferCache;\n}\n\n// ============================================================================\n// Astro Integration API\n// ============================================================================\n\nexport interface TastyIntegrationOptions {\n /**\n * Enable island hydration support.\n *\n * When `true` (default): injects a client hydration script via\n * `injectScript('before-hydration')` and sets `transferCache: true`\n * on the middleware. Islands skip the style pipeline during hydration.\n *\n * When `false`: no client JS is shipped and `transferCache` is set\n * to `false`. Use this for fully static sites without `client:*`\n * directives.\n */\n islands?: boolean;\n}\n\n/**\n * Astro integration that automatically sets up Tasty SSR.\n *\n * Registers middleware for cross-component CSS deduplication and\n * optionally injects a client hydration script for island support.\n *\n * @example Basic setup (with islands)\n * ```ts\n * // astro.config.mjs\n * import { tastyIntegration } from '@tenphi/tasty/ssr/astro';\n *\n * export default defineConfig({\n * integrations: [tastyIntegration()],\n * });\n * ```\n *\n * @example Static-only (no client JS)\n * ```ts\n * // astro.config.mjs\n * import { tastyIntegration } from '@tenphi/tasty/ssr/astro';\n *\n * export default defineConfig({\n * integrations: [tastyIntegration({ islands: false })],\n * });\n * ```\n */\nexport function tastyIntegration(options?: TastyIntegrationOptions) {\n const { islands = true } = options ?? {};\n\n setMiddlewareTransferCache(islands);\n\n return {\n name: '@tenphi/tasty',\n hooks: {\n 'astro:config:setup': ({\n addMiddleware,\n injectScript,\n }: {\n addMiddleware: (middleware: {\n entrypoint: string | URL;\n order: 'pre' | 'post';\n }) => void;\n injectScript: (stage: string, content: string) => void;\n }) => {\n addMiddleware({\n entrypoint: new URL('./astro-middleware.js', import.meta.url),\n order: 'pre',\n });\n\n if (islands) {\n injectScript(\n 'before-hydration',\n `import \"@tenphi/tasty/ssr/astro-client\";`,\n );\n }\n },\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AAmBA,iCAAiC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCjD,SAAgB,gBAAgB,SAAkC;AAChE,QAAO,OACL,UACA,SACsB;EACtB,MAAM,gBAAgB,SAAS,iBAAiB;EAChD,MAAM,YAAY,IAAI,sBAAsB;EAK5C,MAAM,WAAW,MAAM,iBAAiB,WAAW,YAAY;GAC7D,MAAM,WAAW,MAAM,MAAM;GAC7B,MAAM,OAAO,SAAS;AACtB,OAAI,CAAC,KACH,QAAO;IACL,MAAM;IACN,QAAQ,SAAS;IACjB,SAAS,SAAS;IACnB;GAGH,MAAM,SAAS,KAAK,YAAY,IAAI,mBAAmB,CAAC,CAAC,WAAW;GACpE,MAAM,QAAkB,EAAE;AAC1B,YAAS;IACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,MAAM;AAC3C,QAAI,KAAM;AACV,UAAM,KAAK,MAAM;;AAEnB,UAAO;IACL,MAAM,MAAM,KAAK,GAAG;IACpB,QAAQ,SAAS;IACjB,SAAS,SAAS;IACnB;IACD;AAEF,MAAI,CAAC,SAAS,KACZ,QAAO,IAAI,SAAS,MAAM;GACxB,QAAQ,SAAS;GACjB,SAAS,SAAS;GACnB,CAAC;EAGJ,IAAI,EAAE,SAAS;EAEf,MAAM,MAAM,UAAU,QAAQ;AAC9B,MAAI,CAAC,IACH,QAAO,IAAI,SAAS,MAAM;GACxB,QAAQ,SAAS;GACjB,SAAS,SAAS;GACnB,CAAC;EAGJ,MAAM,QAAQ,WAAW,CAAC;EAC1B,MAAM,YAAY,QAAQ,WAAW,MAAM,KAAK;EAChD,MAAM,WAAW,wBAAwB,UAAU,GAAG,IAAI;EAE1D,IAAI,WAAW;AACf,MAAI,eAAe;GACjB,MAAM,aAAa,UAAU,uBAAuB;AACpD,OAAI,WAAW,SAAS,EAEtB,YAAW,UAAU,UAAU,gDADT,WAAW,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,IAAI,CAC8B;;EAIjG,MAAM,YAAY,WAAW;EAC7B,MAAM,MAAM,KAAK,QAAQ,UAAU;AACnC,MAAI,QAAQ,GACV,QAAO,KAAK,MAAM,GAAG,IAAI,GAAG,YAAY,KAAK,MAAM,IAAI;MAEvD,QAAO,YAAY;EAGrB,MAAM,UAAU,IAAI,QAAQ,SAAS,QAAQ;AAC7C,UAAQ,OAAO,iBAAiB;AAEhC,SAAO,IAAI,SAAS,MAAM;GACxB,QAAQ,SAAS;GACjB;GACD,CAAC;;;AASN,IAAI,2BAA2B;;AAG/B,SAAgB,2BAA2B,OAAsB;AAC/D,4BAA2B;;;AAI7B,SAAgB,6BAAsC;AACpD,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDT,SAAgB,iBAAiB,SAAmC;CAClE,MAAM,EAAE,UAAU,SAAS,WAAW,EAAE;AAExC,4BAA2B,QAAQ;AAEnC,QAAO;EACL,MAAM;EACN,OAAO,EACL,uBAAuB,EACrB,eACA,mBAOI;AACJ,iBAAc;IACZ,YAAY,IAAI,IAAI,yBAAyB,OAAO,KAAK,IAAI;IAC7D,OAAO;IACR,CAAC;AAEF,OAAI,QACF,cACE,oBACA,2CACD;KAGN;EACF"}
@@ -0,0 +1,17 @@
1
+ import { ServerStyleCollector } from "./collector.js";
2
+
3
+ //#region src/ssr/async-storage.d.ts
4
+ /**
5
+ * Run a function with a ServerStyleCollector bound to the current
6
+ * async context. All useStyles() calls within `fn` (and any async
7
+ * continuations) will find this collector via getSSRCollector().
8
+ */
9
+ declare function runWithCollector<T>(collector: ServerStyleCollector, fn: () => T): T;
10
+ /**
11
+ * Retrieve the ServerStyleCollector bound to the current async context.
12
+ * Returns null when called outside of runWithCollector() or on the client.
13
+ */
14
+ declare function getSSRCollector(): ServerStyleCollector | null;
15
+ //#endregion
16
+ export { getSSRCollector, runWithCollector };
17
+ //# sourceMappingURL=async-storage.d.ts.map
@@ -0,0 +1,44 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ //#region src/ssr/async-storage.ts
3
+ /**
4
+ * AsyncLocalStorage integration for SSR collector discovery.
5
+ *
6
+ * Used by Astro middleware and generic framework integrations where
7
+ * the library cannot wrap the React tree with a context provider.
8
+ * The middleware calls runWithCollector() around the render, and
9
+ * useStyles() calls getSSRCollector() to find it.
10
+ *
11
+ * Uses globalThis to ensure the AsyncLocalStorage instance is shared
12
+ * across module instances — frameworks like Astro may load middleware
13
+ * and page components from separate module graphs.
14
+ *
15
+ * This module imports from 'node:async_hooks' — it must be excluded
16
+ * from client bundles via the build configuration.
17
+ */
18
+ const ALS_KEY = "__tasty_ssr_als__";
19
+ function getSharedStorage() {
20
+ const g = globalThis;
21
+ if (!g[ALS_KEY]) g[ALS_KEY] = new AsyncLocalStorage();
22
+ return g[ALS_KEY];
23
+ }
24
+ /**
25
+ * Run a function with a ServerStyleCollector bound to the current
26
+ * async context. All useStyles() calls within `fn` (and any async
27
+ * continuations) will find this collector via getSSRCollector().
28
+ */
29
+ function runWithCollector(collector, fn) {
30
+ return getSharedStorage().run(collector, fn);
31
+ }
32
+ /**
33
+ * Retrieve the ServerStyleCollector bound to the current async context.
34
+ * Returns null when called outside of runWithCollector() or on the client.
35
+ */
36
+ function getSSRCollector() {
37
+ const storage = getSharedStorage();
38
+ if (typeof storage?.getStore !== "function") return null;
39
+ return storage.getStore() ?? null;
40
+ }
41
+ //#endregion
42
+ export { getSSRCollector, runWithCollector };
43
+
44
+ //# sourceMappingURL=async-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-storage.js","names":[],"sources":["../../src/ssr/async-storage.ts"],"sourcesContent":["/**\n * AsyncLocalStorage integration for SSR collector discovery.\n *\n * Used by Astro middleware and generic framework integrations where\n * the library cannot wrap the React tree with a context provider.\n * The middleware calls runWithCollector() around the render, and\n * useStyles() calls getSSRCollector() to find it.\n *\n * Uses globalThis to ensure the AsyncLocalStorage instance is shared\n * across module instances — frameworks like Astro may load middleware\n * and page components from separate module graphs.\n *\n * This module imports from 'node:async_hooks' — it must be excluded\n * from client bundles via the build configuration.\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nimport type { ServerStyleCollector } from './collector';\n\nconst ALS_KEY = '__tasty_ssr_als__';\n\nfunction getSharedStorage(): AsyncLocalStorage<ServerStyleCollector> {\n const g = globalThis as Record<string, unknown>;\n if (!g[ALS_KEY]) {\n g[ALS_KEY] = new AsyncLocalStorage<ServerStyleCollector>();\n }\n return g[ALS_KEY] as AsyncLocalStorage<ServerStyleCollector>;\n}\n\n/**\n * Run a function with a ServerStyleCollector bound to the current\n * async context. All useStyles() calls within `fn` (and any async\n * continuations) will find this collector via getSSRCollector().\n */\nexport function runWithCollector<T>(\n collector: ServerStyleCollector,\n fn: () => T,\n): T {\n return getSharedStorage().run(collector, fn);\n}\n\n/**\n * Retrieve the ServerStyleCollector bound to the current async context.\n * Returns null when called outside of runWithCollector() or on the client.\n */\nexport function getSSRCollector(): ServerStyleCollector | null {\n const storage = getSharedStorage();\n if (typeof storage?.getStore !== 'function') return null;\n return storage.getStore() ?? null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAoBA,MAAM,UAAU;AAEhB,SAAS,mBAA4D;CACnE,MAAM,IAAI;AACV,KAAI,CAAC,EAAE,SACL,GAAE,WAAW,IAAI,mBAAyC;AAE5D,QAAO,EAAE;;;;;;;AAQX,SAAgB,iBACd,WACA,IACG;AACH,QAAO,kBAAkB,CAAC,IAAI,WAAW,GAAG;;;;;;AAO9C,SAAgB,kBAA+C;CAC7D,MAAM,UAAU,kBAAkB;AAClC,KAAI,OAAO,SAAS,aAAa,WAAY,QAAO;AACpD,QAAO,QAAQ,UAAU,IAAI"}