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

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 (357) hide show
  1. package/README.md +181 -131
  2. package/dist/{ssr/async-storage.js → async-storage-DKK-wTD4.js} +2 -2
  3. package/dist/async-storage-DKK-wTD4.js.map +1 -0
  4. package/dist/babel-eMs_zZjM.d.ts +83 -0
  5. package/dist/{ssr/collector.js → collector-BjkQEtbh.js} +111 -38
  6. package/dist/collector-BjkQEtbh.js.map +1 -0
  7. package/dist/{ssr/collector.d.ts → collector-DzeNxHOq.d.ts} +58 -7
  8. package/dist/config-DMslHK_e.d.ts +1298 -0
  9. package/dist/config-DX126v_w.js +12318 -0
  10. package/dist/config-DX126v_w.js.map +1 -0
  11. package/dist/{ssr/context.js → context-CA8YKeMn.js} +11 -3
  12. package/dist/context-CA8YKeMn.js.map +1 -0
  13. package/dist/core/index.d.ts +5 -34
  14. package/dist/core/index.js +6 -27
  15. package/dist/core-CMn-Tj2X.js +1573 -0
  16. package/dist/core-CMn-Tj2X.js.map +1 -0
  17. package/dist/{zero/extractor.js → css-writer-C8ShXK5R.js} +148 -24
  18. package/dist/css-writer-C8ShXK5R.js.map +1 -0
  19. package/dist/{ssr/format-global-rules.js → format-global-rules-DklyaXv-.js} +2 -2
  20. package/dist/format-global-rules-DklyaXv-.js.map +1 -0
  21. package/dist/{ssr/format-rules.js → format-rules-BkstDvlN.js} +59 -2
  22. package/dist/format-rules-BkstDvlN.js.map +1 -0
  23. package/dist/{ssr/hydrate.js → hydrate-DXXWZgm0.js} +4 -12
  24. package/dist/hydrate-DXXWZgm0.js.map +1 -0
  25. package/dist/index-BcsaHo8x.d.ts +1568 -0
  26. package/dist/index-CC0wykKn.d.ts +1909 -0
  27. package/dist/index.d.ts +5 -51
  28. package/dist/index.js +922 -36
  29. package/dist/index.js.map +1 -0
  30. package/dist/keyframes-Z0YLVOWq.js +493 -0
  31. package/dist/keyframes-Z0YLVOWq.js.map +1 -0
  32. package/dist/{utils/merge-styles.d.ts → merge-styles-BTFAkYSD.d.ts} +3 -3
  33. package/dist/{utils/merge-styles.js → merge-styles-Dpueyn82.js} +3 -4
  34. package/dist/merge-styles-Dpueyn82.js.map +1 -0
  35. package/dist/{utils/resolve-recipes.js → resolve-recipes-B2Vjr7ET.js} +4 -6
  36. package/dist/resolve-recipes-B2Vjr7ET.js.map +1 -0
  37. package/dist/ssr/astro-client.js +1 -1
  38. package/dist/ssr/astro-client.js.map +1 -1
  39. package/dist/ssr/astro-middleware-static.d.ts +16 -0
  40. package/dist/ssr/astro-middleware-static.js +18 -0
  41. package/dist/ssr/astro-middleware-static.js.map +1 -0
  42. package/dist/ssr/astro-middleware.d.ts +8 -6
  43. package/dist/ssr/astro-middleware.js +10 -10
  44. package/dist/ssr/astro-middleware.js.map +1 -1
  45. package/dist/ssr/astro.d.ts +2 -17
  46. package/dist/ssr/astro.js +29 -21
  47. package/dist/ssr/astro.js.map +1 -1
  48. package/dist/ssr/index.d.ts +37 -4
  49. package/dist/ssr/index.js +5 -5
  50. package/dist/ssr/index.js.map +1 -1
  51. package/dist/ssr/next.d.ts +2 -3
  52. package/dist/ssr/next.js +7 -8
  53. package/dist/ssr/next.js.map +1 -1
  54. package/dist/static/index.d.ts +91 -5
  55. package/dist/static/index.js +49 -3
  56. package/dist/static/index.js.map +1 -0
  57. package/dist/static/inject.js.map +1 -1
  58. package/dist/zero/babel.d.ts +2 -81
  59. package/dist/zero/babel.js +45 -29
  60. package/dist/zero/babel.js.map +1 -1
  61. package/dist/zero/index.d.ts +81 -3
  62. package/dist/zero/index.js +2 -3
  63. package/dist/zero/next.d.ts +1 -1
  64. package/dist/zero/next.js +1 -1
  65. package/dist/zero/next.js.map +1 -1
  66. package/docs/README.md +18 -10
  67. package/docs/adoption.md +56 -31
  68. package/docs/ai-agents.md +224 -0
  69. package/docs/comparison.md +69 -40
  70. package/docs/configuration.md +501 -72
  71. package/docs/debug.md +22 -18
  72. package/docs/design-system.md +43 -24
  73. package/docs/dsl.md +512 -179
  74. package/docs/getting-started.md +60 -39
  75. package/docs/injector.md +110 -32
  76. package/docs/methodology.md +68 -36
  77. package/docs/migration-v3.md +285 -0
  78. package/docs/pipeline.md +741 -0
  79. package/docs/plugins.md +353 -0
  80. package/docs/react-api.md +164 -40
  81. package/docs/runtime-benchmarks.md +216 -0
  82. package/docs/ssr.md +92 -81
  83. package/docs/styles.md +65 -16
  84. package/docs/tasty-static.md +149 -99
  85. package/package.json +49 -21
  86. package/tasty.config.ts +6 -0
  87. package/dist/_virtual/_rolldown/runtime.js +0 -7
  88. package/dist/chunks/cacheKey.d.ts +0 -1
  89. package/dist/chunks/cacheKey.js +0 -77
  90. package/dist/chunks/cacheKey.js.map +0 -1
  91. package/dist/chunks/definitions.d.ts +0 -37
  92. package/dist/chunks/definitions.js +0 -258
  93. package/dist/chunks/definitions.js.map +0 -1
  94. package/dist/chunks/index.d.ts +0 -1
  95. package/dist/chunks/renderChunk.d.ts +0 -1
  96. package/dist/chunks/renderChunk.js +0 -59
  97. package/dist/chunks/renderChunk.js.map +0 -1
  98. package/dist/compute-styles.d.ts +0 -31
  99. package/dist/compute-styles.js +0 -335
  100. package/dist/compute-styles.js.map +0 -1
  101. package/dist/config.d.ts +0 -407
  102. package/dist/config.js +0 -587
  103. package/dist/config.js.map +0 -1
  104. package/dist/counter-style/index.js +0 -51
  105. package/dist/counter-style/index.js.map +0 -1
  106. package/dist/debug.d.ts +0 -89
  107. package/dist/debug.js +0 -453
  108. package/dist/debug.js.map +0 -1
  109. package/dist/font-face/index.js +0 -63
  110. package/dist/font-face/index.js.map +0 -1
  111. package/dist/hooks/index.d.ts +0 -7
  112. package/dist/hooks/useCounterStyle.d.ts +0 -36
  113. package/dist/hooks/useCounterStyle.js +0 -65
  114. package/dist/hooks/useCounterStyle.js.map +0 -1
  115. package/dist/hooks/useFontFace.d.ts +0 -45
  116. package/dist/hooks/useFontFace.js +0 -66
  117. package/dist/hooks/useFontFace.js.map +0 -1
  118. package/dist/hooks/useGlobalStyles.d.ts +0 -46
  119. package/dist/hooks/useGlobalStyles.js +0 -88
  120. package/dist/hooks/useGlobalStyles.js.map +0 -1
  121. package/dist/hooks/useKeyframes.d.ts +0 -58
  122. package/dist/hooks/useKeyframes.js +0 -55
  123. package/dist/hooks/useKeyframes.js.map +0 -1
  124. package/dist/hooks/useProperty.d.ts +0 -81
  125. package/dist/hooks/useProperty.js +0 -96
  126. package/dist/hooks/useProperty.js.map +0 -1
  127. package/dist/hooks/useRawCSS.d.ts +0 -22
  128. package/dist/hooks/useRawCSS.js +0 -103
  129. package/dist/hooks/useRawCSS.js.map +0 -1
  130. package/dist/hooks/useStyles.d.ts +0 -40
  131. package/dist/hooks/useStyles.js +0 -31
  132. package/dist/hooks/useStyles.js.map +0 -1
  133. package/dist/injector/index.d.ts +0 -182
  134. package/dist/injector/index.js +0 -185
  135. package/dist/injector/index.js.map +0 -1
  136. package/dist/injector/injector.d.ts +0 -198
  137. package/dist/injector/injector.js +0 -651
  138. package/dist/injector/injector.js.map +0 -1
  139. package/dist/injector/sheet-manager.d.ts +0 -132
  140. package/dist/injector/sheet-manager.js +0 -699
  141. package/dist/injector/sheet-manager.js.map +0 -1
  142. package/dist/injector/types.d.ts +0 -235
  143. package/dist/keyframes/index.js +0 -206
  144. package/dist/keyframes/index.js.map +0 -1
  145. package/dist/parser/classify.js +0 -319
  146. package/dist/parser/classify.js.map +0 -1
  147. package/dist/parser/const.js +0 -60
  148. package/dist/parser/const.js.map +0 -1
  149. package/dist/parser/lru.js +0 -109
  150. package/dist/parser/lru.js.map +0 -1
  151. package/dist/parser/parser.d.ts +0 -25
  152. package/dist/parser/parser.js +0 -115
  153. package/dist/parser/parser.js.map +0 -1
  154. package/dist/parser/tokenizer.js +0 -69
  155. package/dist/parser/tokenizer.js.map +0 -1
  156. package/dist/parser/types.d.ts +0 -51
  157. package/dist/parser/types.js +0 -46
  158. package/dist/parser/types.js.map +0 -1
  159. package/dist/pipeline/conditions.d.ts +0 -134
  160. package/dist/pipeline/conditions.js +0 -406
  161. package/dist/pipeline/conditions.js.map +0 -1
  162. package/dist/pipeline/exclusive.js +0 -230
  163. package/dist/pipeline/exclusive.js.map +0 -1
  164. package/dist/pipeline/index.d.ts +0 -55
  165. package/dist/pipeline/index.js +0 -708
  166. package/dist/pipeline/index.js.map +0 -1
  167. package/dist/pipeline/materialize.js +0 -1103
  168. package/dist/pipeline/materialize.js.map +0 -1
  169. package/dist/pipeline/parseStateKey.d.ts +0 -15
  170. package/dist/pipeline/parseStateKey.js +0 -446
  171. package/dist/pipeline/parseStateKey.js.map +0 -1
  172. package/dist/pipeline/simplify.js +0 -515
  173. package/dist/pipeline/simplify.js.map +0 -1
  174. package/dist/pipeline/warnings.js +0 -18
  175. package/dist/pipeline/warnings.js.map +0 -1
  176. package/dist/plugins/index.d.ts +0 -2
  177. package/dist/plugins/okhsl-plugin.d.ts +0 -35
  178. package/dist/plugins/okhsl-plugin.js +0 -97
  179. package/dist/plugins/okhsl-plugin.js.map +0 -1
  180. package/dist/plugins/types.d.ts +0 -87
  181. package/dist/properties/index.js +0 -222
  182. package/dist/properties/index.js.map +0 -1
  183. package/dist/properties/property-type-resolver.d.ts +0 -24
  184. package/dist/properties/property-type-resolver.js +0 -90
  185. package/dist/properties/property-type-resolver.js.map +0 -1
  186. package/dist/rsc-cache.js +0 -79
  187. package/dist/rsc-cache.js.map +0 -1
  188. package/dist/ssr/async-storage.d.ts +0 -17
  189. package/dist/ssr/async-storage.js.map +0 -1
  190. package/dist/ssr/collect-auto-properties.js +0 -58
  191. package/dist/ssr/collect-auto-properties.js.map +0 -1
  192. package/dist/ssr/collector.js.map +0 -1
  193. package/dist/ssr/context.js.map +0 -1
  194. package/dist/ssr/format-global-rules.js.map +0 -1
  195. package/dist/ssr/format-keyframes.js +0 -69
  196. package/dist/ssr/format-keyframes.js.map +0 -1
  197. package/dist/ssr/format-property.js +0 -49
  198. package/dist/ssr/format-property.js.map +0 -1
  199. package/dist/ssr/format-rules.js.map +0 -1
  200. package/dist/ssr/hydrate.d.ts +0 -29
  201. package/dist/ssr/hydrate.js.map +0 -1
  202. package/dist/ssr/ssr-collector-ref.js +0 -29
  203. package/dist/ssr/ssr-collector-ref.js.map +0 -1
  204. package/dist/states/index.d.ts +0 -49
  205. package/dist/states/index.js +0 -170
  206. package/dist/states/index.js.map +0 -1
  207. package/dist/static/tastyStatic.d.ts +0 -46
  208. package/dist/static/tastyStatic.js +0 -30
  209. package/dist/static/tastyStatic.js.map +0 -1
  210. package/dist/static/types.d.ts +0 -49
  211. package/dist/static/types.js +0 -24
  212. package/dist/static/types.js.map +0 -1
  213. package/dist/styles/border.d.ts +0 -25
  214. package/dist/styles/border.js +0 -120
  215. package/dist/styles/border.js.map +0 -1
  216. package/dist/styles/color.d.ts +0 -14
  217. package/dist/styles/color.js +0 -26
  218. package/dist/styles/color.js.map +0 -1
  219. package/dist/styles/const.js +0 -17
  220. package/dist/styles/const.js.map +0 -1
  221. package/dist/styles/createStyle.js +0 -79
  222. package/dist/styles/createStyle.js.map +0 -1
  223. package/dist/styles/dimension.js +0 -109
  224. package/dist/styles/dimension.js.map +0 -1
  225. package/dist/styles/directional.js +0 -133
  226. package/dist/styles/directional.js.map +0 -1
  227. package/dist/styles/display.d.ts +0 -30
  228. package/dist/styles/display.js +0 -73
  229. package/dist/styles/display.js.map +0 -1
  230. package/dist/styles/fade.d.ts +0 -15
  231. package/dist/styles/fade.js +0 -62
  232. package/dist/styles/fade.js.map +0 -1
  233. package/dist/styles/fill.d.ts +0 -42
  234. package/dist/styles/fill.js +0 -51
  235. package/dist/styles/fill.js.map +0 -1
  236. package/dist/styles/flow.d.ts +0 -16
  237. package/dist/styles/flow.js +0 -12
  238. package/dist/styles/flow.js.map +0 -1
  239. package/dist/styles/gap.d.ts +0 -31
  240. package/dist/styles/gap.js +0 -38
  241. package/dist/styles/gap.js.map +0 -1
  242. package/dist/styles/height.d.ts +0 -17
  243. package/dist/styles/height.js +0 -19
  244. package/dist/styles/height.js.map +0 -1
  245. package/dist/styles/index.d.ts +0 -1
  246. package/dist/styles/index.js +0 -8
  247. package/dist/styles/index.js.map +0 -1
  248. package/dist/styles/inset.d.ts +0 -24
  249. package/dist/styles/inset.js +0 -34
  250. package/dist/styles/inset.js.map +0 -1
  251. package/dist/styles/list.d.ts +0 -16
  252. package/dist/styles/list.js +0 -100
  253. package/dist/styles/list.js.map +0 -1
  254. package/dist/styles/margin.d.ts +0 -24
  255. package/dist/styles/margin.js +0 -32
  256. package/dist/styles/margin.js.map +0 -1
  257. package/dist/styles/outline.d.ts +0 -29
  258. package/dist/styles/outline.js +0 -55
  259. package/dist/styles/outline.js.map +0 -1
  260. package/dist/styles/padding.d.ts +0 -24
  261. package/dist/styles/padding.js +0 -32
  262. package/dist/styles/padding.js.map +0 -1
  263. package/dist/styles/placement.d.ts +0 -37
  264. package/dist/styles/placement.js +0 -74
  265. package/dist/styles/placement.js.map +0 -1
  266. package/dist/styles/predefined.d.ts +0 -71
  267. package/dist/styles/predefined.js +0 -237
  268. package/dist/styles/predefined.js.map +0 -1
  269. package/dist/styles/preset.d.ts +0 -52
  270. package/dist/styles/preset.js +0 -127
  271. package/dist/styles/preset.js.map +0 -1
  272. package/dist/styles/radius.d.ts +0 -12
  273. package/dist/styles/radius.js +0 -83
  274. package/dist/styles/radius.js.map +0 -1
  275. package/dist/styles/scrollMargin.d.ts +0 -24
  276. package/dist/styles/scrollMargin.js +0 -32
  277. package/dist/styles/scrollMargin.js.map +0 -1
  278. package/dist/styles/scrollbar.d.ts +0 -25
  279. package/dist/styles/scrollbar.js +0 -51
  280. package/dist/styles/scrollbar.js.map +0 -1
  281. package/dist/styles/shadow.d.ts +0 -14
  282. package/dist/styles/shadow.js +0 -25
  283. package/dist/styles/shadow.js.map +0 -1
  284. package/dist/styles/shared.js +0 -17
  285. package/dist/styles/shared.js.map +0 -1
  286. package/dist/styles/transition.d.ts +0 -14
  287. package/dist/styles/transition.js +0 -159
  288. package/dist/styles/transition.js.map +0 -1
  289. package/dist/styles/types.d.ts +0 -564
  290. package/dist/styles/width.d.ts +0 -17
  291. package/dist/styles/width.js +0 -19
  292. package/dist/styles/width.js.map +0 -1
  293. package/dist/tasty.d.ts +0 -134
  294. package/dist/tasty.js +0 -248
  295. package/dist/tasty.js.map +0 -1
  296. package/dist/types.d.ts +0 -184
  297. package/dist/utils/cache-wrapper.js +0 -21
  298. package/dist/utils/cache-wrapper.js.map +0 -1
  299. package/dist/utils/case-converter.js +0 -8
  300. package/dist/utils/case-converter.js.map +0 -1
  301. package/dist/utils/color-math.d.ts +0 -46
  302. package/dist/utils/color-math.js +0 -749
  303. package/dist/utils/color-math.js.map +0 -1
  304. package/dist/utils/color-space.d.ts +0 -5
  305. package/dist/utils/color-space.js +0 -228
  306. package/dist/utils/color-space.js.map +0 -1
  307. package/dist/utils/colors.d.ts +0 -5
  308. package/dist/utils/colors.js +0 -10
  309. package/dist/utils/colors.js.map +0 -1
  310. package/dist/utils/css-types.d.ts +0 -7
  311. package/dist/utils/deps-equal.js +0 -15
  312. package/dist/utils/deps-equal.js.map +0 -1
  313. package/dist/utils/dotize.d.ts +0 -26
  314. package/dist/utils/dotize.js +0 -122
  315. package/dist/utils/dotize.js.map +0 -1
  316. package/dist/utils/filter-base-props.d.ts +0 -15
  317. package/dist/utils/filter-base-props.js +0 -45
  318. package/dist/utils/filter-base-props.js.map +0 -1
  319. package/dist/utils/get-display-name.d.ts +0 -7
  320. package/dist/utils/get-display-name.js +0 -10
  321. package/dist/utils/get-display-name.js.map +0 -1
  322. package/dist/utils/has-keys.js +0 -13
  323. package/dist/utils/has-keys.js.map +0 -1
  324. package/dist/utils/hash.js +0 -14
  325. package/dist/utils/hash.js.map +0 -1
  326. package/dist/utils/is-dev-env.js +0 -19
  327. package/dist/utils/is-dev-env.js.map +0 -1
  328. package/dist/utils/is-valid-element-type.js +0 -15
  329. package/dist/utils/is-valid-element-type.js.map +0 -1
  330. package/dist/utils/merge-styles.js.map +0 -1
  331. package/dist/utils/mod-attrs.d.ts +0 -6
  332. package/dist/utils/mod-attrs.js +0 -20
  333. package/dist/utils/mod-attrs.js.map +0 -1
  334. package/dist/utils/process-tokens.d.ts +0 -17
  335. package/dist/utils/process-tokens.js +0 -83
  336. package/dist/utils/process-tokens.js.map +0 -1
  337. package/dist/utils/resolve-recipes.d.ts +0 -17
  338. package/dist/utils/resolve-recipes.js.map +0 -1
  339. package/dist/utils/selector-transform.js +0 -32
  340. package/dist/utils/selector-transform.js.map +0 -1
  341. package/dist/utils/string.js +0 -8
  342. package/dist/utils/string.js.map +0 -1
  343. package/dist/utils/styles.d.ts +0 -99
  344. package/dist/utils/styles.js +0 -220
  345. package/dist/utils/styles.js.map +0 -1
  346. package/dist/utils/typography.d.ts +0 -58
  347. package/dist/utils/typography.js +0 -51
  348. package/dist/utils/typography.js.map +0 -1
  349. package/dist/utils/warnings.d.ts +0 -16
  350. package/dist/utils/warnings.js +0 -16
  351. package/dist/utils/warnings.js.map +0 -1
  352. package/dist/zero/css-writer.d.ts +0 -45
  353. package/dist/zero/css-writer.js +0 -73
  354. package/dist/zero/css-writer.js.map +0 -1
  355. package/dist/zero/extractor.d.ts +0 -24
  356. package/dist/zero/extractor.js.map +0 -1
  357. package/docs/PIPELINE.md +0 -519
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-recipes-B2Vjr7ET.js","names":[],"sources":["../src/utils/resolve-recipes.ts"],"sourcesContent":["/**\n * Recipe resolution utility.\n *\n * Resolves `recipe` style properties by looking up predefined recipe styles\n * from global configuration and merging them with the component's own styles.\n *\n * Resolution order per level (top-level and each sub-element independently):\n * base_recipe_1 base_recipe_2 → component styles → post_recipe_1 post_recipe_2\n *\n * The `/` separator splits base recipes (before component styles)\n * from post recipes (after component styles). All merges use mergeStyles\n * semantics: primitives and state maps with '' key fully replace;\n * state maps without '' key extend the existing value.\n *\n * Returns the same object reference if no recipes are present (zero overhead).\n */\n\nimport { getGlobalRecipes } from '../config';\nimport { isSelector } from '../pipeline';\nimport type { RecipeStyles, Styles } from '../styles/types';\n\nimport { isDevEnv } from './is-dev-env';\nimport { mergeStyles } from './merge-styles';\n\nconst devMode = isDevEnv();\n\ninterface ParsedRecipeGroups {\n base: string[] | null;\n post: string[] | null;\n}\n\n/**\n * Parse a recipe string into base and post recipe name groups.\n *\n * Syntax: `'base1 base2 / post1 post2'`\n * - Names are space-separated within each group\n * - `/` separates base (before component) from post (after component) groups\n * - `/` is optional; if absent, all names are base\n * - `none` as the sole base value means \"no base recipes\"\n *\n * Returns `{ base: null, post: null }` if the string is empty or invalid.\n */\nfunction parseRecipeNames(value: unknown): ParsedRecipeGroups {\n const empty: ParsedRecipeGroups = { base: null, post: null };\n\n if (typeof value !== 'string') return empty;\n const trimmed = value.trim();\n if (trimmed === '') return empty;\n\n const slashIndex = trimmed.indexOf('/');\n\n if (slashIndex === -1) {\n if (trimmed === 'none') return empty;\n const names = splitNames(trimmed);\n return { base: names, post: null };\n }\n\n const basePart = trimmed.slice(0, slashIndex);\n const postPart = trimmed.slice(slashIndex + 1);\n\n return {\n base: basePart.trim() === 'none' ? null : splitNames(basePart),\n post: splitNames(postPart),\n };\n}\n\nfunction splitNames(s: string): string[] | null {\n const names = s.split(/\\s+/).filter(Boolean);\n return names.length > 0 ? names : null;\n}\n\n/**\n * Collect merged styles for a list of recipe names.\n * Each recipe is flat-spread on top of the previous.\n */\nfunction collectRecipeStyles(\n names: string[],\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> {\n let merged: Record<string, unknown> = {};\n\n for (const name of names) {\n const recipeStyles = recipes[name];\n\n if (!recipeStyles) {\n if (devMode) {\n console.warn(\n `[Tasty] Recipe \"${name}\" not found. ` +\n `Make sure it is defined in configure({ recipes: { ... } }).`,\n );\n }\n continue;\n }\n\n merged = { ...merged, ...(recipeStyles as Record<string, unknown>) };\n }\n\n return merged;\n}\n\n/**\n * Resolve recipe references in a flat styles object (no sub-elements).\n * Returns null if no `recipe` key is present.\n *\n * Resolution: base recipes → component styles → post recipes (all via mergeStyles)\n */\nfunction resolveRecipesForLevel(\n styles: Record<string, unknown>,\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> | null {\n if (!('recipe' in styles)) return null;\n\n const { base, post } = parseRecipeNames(styles.recipe);\n\n // Separate selector keys (sub-elements) from flat style properties.\n // mergeStyles handles selectors with its own semantics (e.g. false = delete),\n // but at this level we only want recipe merging on flat properties.\n\n const { recipe: _recipe, ...allRest } = styles;\n const flatStyles: Record<string, unknown> = {};\n const selectorStyles: Record<string, unknown> = {};\n\n for (const key of Object.keys(allRest)) {\n if (isSelector(key)) {\n selectorStyles[key] = allRest[key];\n } else {\n flatStyles[key] = allRest[key];\n }\n }\n\n if (!base && !post) {\n return allRest;\n }\n\n // 1. Merge base recipes, then component styles on top (via mergeStyles)\n let result: Record<string, unknown>;\n\n if (base) {\n const baseStyles = collectRecipeStyles(base, recipes);\n result = mergeStyles(baseStyles as Styles, flatStyles as Styles) as Record<\n string,\n unknown\n >;\n } else {\n result = { ...flatStyles };\n }\n\n // 2. Apply post recipes via mergeStyles (state map extend semantics)\n if (post) {\n const postStyles = collectRecipeStyles(post, recipes);\n result = mergeStyles(result as Styles, postStyles as Styles) as Record<\n string,\n unknown\n >;\n }\n\n // Re-attach selector keys unchanged\n for (const key of Object.keys(selectorStyles)) {\n result[key] = selectorStyles[key];\n }\n\n return result;\n}\n\n/**\n * Resolve all `recipe` style properties in a styles object.\n *\n * Handles both top-level and sub-element recipe references.\n * Returns the same object reference if no recipes are present anywhere\n * (zero overhead for the common case).\n *\n * @param styles - The styles object potentially containing `recipe` keys\n * @returns Resolved styles with recipe values merged in, or the original object if unchanged\n */\nexport function resolveRecipes(styles: Styles): Styles {\n const recipes = getGlobalRecipes();\n\n // Fast path: no recipes configured globally\n if (!recipes) return styles;\n\n let changed = false;\n\n // Resolve top-level recipe\n const topResolved = resolveRecipesForLevel(\n styles as Record<string, unknown>,\n recipes,\n );\n\n let result: Record<string, unknown>;\n\n if (topResolved) {\n changed = true;\n result = topResolved;\n } else {\n // Keep reference; a shallow copy is deferred until a sub-element actually changes\n result = styles as Record<string, unknown>;\n }\n\n // Resolve sub-element recipes\n const keys = Object.keys(result);\n\n for (const key of keys) {\n if (!isSelector(key)) continue;\n\n const subStyles = result[key];\n\n if (\n !subStyles ||\n typeof subStyles !== 'object' ||\n Array.isArray(subStyles)\n ) {\n continue;\n }\n\n const subRecord = subStyles as Record<string, unknown>;\n\n if (!('recipe' in subRecord)) continue;\n\n const subResolved = resolveRecipesForLevel(subRecord, recipes);\n\n if (subResolved) {\n if (!changed) {\n // First change in sub-elements -- need to shallow-copy the top level\n changed = true;\n result = { ...(styles as Record<string, unknown>) };\n }\n result[key] = subResolved;\n }\n }\n\n return changed ? (result as Styles) : styles;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAwBA,MAAM,UAAU,SAAS;;;;;;;;;;;;AAkBzB,SAAS,iBAAiB,OAAoC;CAC5D,MAAM,QAA4B;EAAE,MAAM;EAAM,MAAM;CAAK;CAE3D,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,YAAY,IAAI,OAAO;CAE3B,MAAM,aAAa,QAAQ,QAAQ,GAAG;CAEtC,IAAI,eAAe,IAAI;EACrB,IAAI,YAAY,QAAQ,OAAO;EAE/B,OAAO;GAAE,MADK,WAAW,OACN;GAAG,MAAM;EAAK;CACnC;CAEA,MAAM,WAAW,QAAQ,MAAM,GAAG,UAAU;CAC5C,MAAM,WAAW,QAAQ,MAAM,aAAa,CAAC;CAE7C,OAAO;EACL,MAAM,SAAS,KAAK,MAAM,SAAS,OAAO,WAAW,QAAQ;EAC7D,MAAM,WAAW,QAAQ;CAC3B;AACF;AAEA,SAAS,WAAW,GAA4B;CAC9C,MAAM,QAAQ,EAAE,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;CAC3C,OAAO,MAAM,SAAS,IAAI,QAAQ;AACpC;;;;;AAMA,SAAS,oBACP,OACA,SACyB;CACzB,IAAI,SAAkC,CAAC;CAEvC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,eAAe,QAAQ;EAE7B,IAAI,CAAC,cAAc;GACjB,IAAI,SACF,QAAQ,KACN,mBAAmB,KAAK,yEAE1B;GAEF;EACF;EAEA,SAAS;GAAE,GAAG;GAAQ,GAAI;EAAyC;CACrE;CAEA,OAAO;AACT;;;;;;;AAQA,SAAS,uBACP,QACA,SACgC;CAChC,IAAI,EAAE,YAAY,SAAS,OAAO;CAElC,MAAM,EAAE,MAAM,SAAS,iBAAiB,OAAO,MAAM;CAMrD,MAAM,EAAE,QAAQ,SAAS,GAAG,YAAY;CACxC,MAAM,aAAsC,CAAC;CAC7C,MAAM,iBAA0C,CAAC;CAEjD,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GACnC,IAAI,WAAW,GAAG,GAChB,eAAe,OAAO,QAAQ;MAE9B,WAAW,OAAO,QAAQ;CAI9B,IAAI,CAAC,QAAQ,CAAC,MACZ,OAAO;CAIT,IAAI;CAEJ,IAAI,MAEF,SAAS,YADU,oBAAoB,MAAM,OACf,GAAa,UAAoB;MAK/D,SAAS,EAAE,GAAG,WAAW;CAI3B,IAAI,MAAM;EACR,MAAM,aAAa,oBAAoB,MAAM,OAAO;EACpD,SAAS,YAAY,QAAkB,UAAoB;CAI7D;CAGA,KAAK,MAAM,OAAO,OAAO,KAAK,cAAc,GAC1C,OAAO,OAAO,eAAe;CAG/B,OAAO;AACT;;;;;;;;;;;AAYA,SAAgB,eAAe,QAAwB;CACrD,MAAM,UAAU,iBAAiB;CAGjC,IAAI,CAAC,SAAS,OAAO;CAErB,IAAI,UAAU;CAGd,MAAM,cAAc,uBAClB,QACA,OACF;CAEA,IAAI;CAEJ,IAAI,aAAa;EACf,UAAU;EACV,SAAS;CACX,OAEE,SAAS;CAIX,MAAM,OAAO,OAAO,KAAK,MAAM;CAE/B,KAAK,MAAM,OAAO,MAAM;EACtB,IAAI,CAAC,WAAW,GAAG,GAAG;EAEtB,MAAM,YAAY,OAAO;EAEzB,IACE,CAAC,aACD,OAAO,cAAc,YACrB,MAAM,QAAQ,SAAS,GAEvB;EAGF,MAAM,YAAY;EAElB,IAAI,EAAE,YAAY,YAAY;EAE9B,MAAM,cAAc,uBAAuB,WAAW,OAAO;EAE7D,IAAI,aAAa;GACf,IAAI,CAAC,SAAS;IAEZ,UAAU;IACV,SAAS,EAAE,GAAI,OAAmC;GACpD;GACA,OAAO,OAAO;EAChB;CACF;CAEA,OAAO,UAAW,SAAoB;AACxC"}
@@ -1,4 +1,4 @@
1
- import { hydrateTastyClasses } from "./hydrate.js";
1
+ import { t as hydrateTastyClasses } from "../hydrate-DXXWZgm0.js";
2
2
  //#region src/ssr/astro-client.ts
3
3
  /**
4
4
  * Client-side cache hydration for Astro islands.
@@ -1 +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"}
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,aACpB,oBAAoB"}
@@ -0,0 +1,16 @@
1
+ //#region src/ssr/astro-middleware-static.d.ts
2
+ /**
3
+ * Astro middleware entrypoint for `tastyIntegration({ islands: false })`.
4
+ *
5
+ * Registered via `addMiddleware()` as
6
+ * `@tenphi/tasty/ssr/astro-middleware-static`. Exposed as a package subpath
7
+ * only so Astro can resolve it — use `tastyMiddleware({ transferCache: false })`
8
+ * directly if you need manual middleware composition.
9
+ *
10
+ * Identical to `./astro-middleware` except that no class-list `<script>` is
11
+ * emitted, so a site without `client:*` islands ships zero client JavaScript.
12
+ */
13
+ declare const onRequest: (_context: unknown, next: () => Promise<Response>) => Promise<Response>;
14
+ //#endregion
15
+ export { onRequest };
16
+ //# sourceMappingURL=astro-middleware-static.d.ts.map
@@ -0,0 +1,18 @@
1
+ import { tastyMiddleware } from "./astro.js";
2
+ //#region src/ssr/astro-middleware-static.ts
3
+ /**
4
+ * Astro middleware entrypoint for `tastyIntegration({ islands: false })`.
5
+ *
6
+ * Registered via `addMiddleware()` as
7
+ * `@tenphi/tasty/ssr/astro-middleware-static`. Exposed as a package subpath
8
+ * only so Astro can resolve it — use `tastyMiddleware({ transferCache: false })`
9
+ * directly if you need manual middleware composition.
10
+ *
11
+ * Identical to `./astro-middleware` except that no class-list `<script>` is
12
+ * emitted, so a site without `client:*` islands ships zero client JavaScript.
13
+ */
14
+ const onRequest = tastyMiddleware({ transferCache: false });
15
+ //#endregion
16
+ export { onRequest };
17
+
18
+ //# sourceMappingURL=astro-middleware-static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astro-middleware-static.js","names":[],"sources":["../../src/ssr/astro-middleware-static.ts"],"sourcesContent":["/**\n * Astro middleware entrypoint for `tastyIntegration({ islands: false })`.\n *\n * Registered via `addMiddleware()` as\n * `@tenphi/tasty/ssr/astro-middleware-static`. Exposed as a package subpath\n * only so Astro can resolve it — use `tastyMiddleware({ transferCache: false })`\n * directly if you need manual middleware composition.\n *\n * Identical to `./astro-middleware` except that no class-list `<script>` is\n * emitted, so a site without `client:*` islands ships zero client JavaScript.\n */\n\nimport { tastyMiddleware } from './astro';\n\nexport const onRequest = tastyMiddleware({ transferCache: false });\n"],"mappings":";;;;;;;;;;;;;AAcA,MAAa,YAAY,gBAAgB,EAAE,eAAe,MAAM,CAAC"}
@@ -1,13 +1,15 @@
1
1
  //#region src/ssr/astro-middleware.d.ts
2
2
  /**
3
- * Astro middleware entrypoint for tastyIntegration().
3
+ * Astro middleware entrypoint for `tastyIntegration()`.
4
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.
5
+ * Registered via `addMiddleware()` as `@tenphi/tasty/ssr/astro-middleware`.
6
+ * Exposed as a package subpath only so Astro can resolve it — use
7
+ * `tastyMiddleware()` directly if you need manual middleware composition.
8
8
  *
9
- * The transferCache setting is controlled by setMiddlewareTransferCache(),
10
- * called by tastyIntegration() before middleware is loaded.
9
+ * This is the islands variant: the rendered class list is transferred to the
10
+ * client so hydrating islands skip the style pipeline. See
11
+ * `./astro-middleware-static` for the variant used by
12
+ * `tastyIntegration({ islands: false })`.
11
13
  */
12
14
  declare const onRequest: (_context: unknown, next: () => Promise<Response>) => Promise<Response>;
13
15
  //#endregion
@@ -1,18 +1,18 @@
1
- import { getMiddlewareTransferCache, tastyMiddleware } from "./astro.js";
1
+ import { tastyMiddleware } from "./astro.js";
2
2
  //#region src/ssr/astro-middleware.ts
3
3
  /**
4
- * Astro middleware entrypoint for tastyIntegration().
4
+ * Astro middleware entrypoint for `tastyIntegration()`.
5
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.
6
+ * Registered via `addMiddleware()` as `@tenphi/tasty/ssr/astro-middleware`.
7
+ * Exposed as a package subpath only so Astro can resolve it — use
8
+ * `tastyMiddleware()` directly if you need manual middleware composition.
9
9
  *
10
- * The transferCache setting is controlled by setMiddlewareTransferCache(),
11
- * called by tastyIntegration() before middleware is loaded.
10
+ * This is the islands variant: the rendered class list is transferred to the
11
+ * client so hydrating islands skip the style pipeline. See
12
+ * `./astro-middleware-static` for the variant used by
13
+ * `tastyIntegration({ islands: false })`.
12
14
  */
13
- const onRequest = tastyMiddleware({ get transferCache() {
14
- return getMiddlewareTransferCache();
15
- } });
15
+ const onRequest = tastyMiddleware({ transferCache: true });
16
16
  //#endregion
17
17
  export { onRequest };
18
18
 
@@ -1 +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"}
1
+ {"version":3,"file":"astro-middleware.js","names":[],"sources":["../../src/ssr/astro-middleware.ts"],"sourcesContent":["/**\n * Astro middleware entrypoint for `tastyIntegration()`.\n *\n * Registered via `addMiddleware()` as `@tenphi/tasty/ssr/astro-middleware`.\n * Exposed as a package subpath only so Astro can resolve it — use\n * `tastyMiddleware()` directly if you need manual middleware composition.\n *\n * This is the islands variant: the rendered class list is transferred to the\n * client so hydrating islands skip the style pipeline. See\n * `./astro-middleware-static` for the variant used by\n * `tastyIntegration({ islands: false })`.\n */\n\nimport { tastyMiddleware } from './astro';\n\nexport const onRequest = tastyMiddleware({ transferCache: true });\n"],"mappings":";;;;;;;;;;;;;;AAeA,MAAa,YAAY,gBAAgB,EAAE,eAAe,KAAK,CAAC"}
@@ -44,10 +44,6 @@ interface TastyMiddlewareOptions {
44
44
  * ```
45
45
  */
46
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
47
  interface TastyIntegrationOptions {
52
48
  /**
53
49
  * Enable island hydration support.
@@ -90,19 +86,8 @@ interface TastyIntegrationOptions {
90
86
  */
91
87
  declare function tastyIntegration(options?: TastyIntegrationOptions): {
92
88
  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
- };
89
+ hooks: Record<string, (...args: never[]) => void>;
105
90
  };
106
91
  //#endregion
107
- export { TastyIntegrationOptions, TastyMiddlewareOptions, getMiddlewareTransferCache, setMiddlewareTransferCache, tastyIntegration, tastyMiddleware };
92
+ export { TastyIntegrationOptions, TastyMiddlewareOptions, tastyIntegration, tastyMiddleware };
108
93
  //# sourceMappingURL=astro.d.ts.map
package/dist/ssr/astro.js CHANGED
@@ -1,7 +1,7 @@
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";
1
+ import { n as getConfig } from "../config-DX126v_w.js";
2
+ import { a as registerSSRCollectorGetterGlobal } from "../format-rules-BkstDvlN.js";
3
+ import { t as ServerStyleCollector } from "../collector-BjkQEtbh.js";
4
+ import { n as runWithCollector, t as getSSRCollector } from "../async-storage-DKK-wTD4.js";
5
5
  //#region src/ssr/astro.ts
6
6
  /**
7
7
  * Astro integration for Tasty SSR.
@@ -46,11 +46,8 @@ function tastyMiddleware(options) {
46
46
  const rendered = await runWithCollector(collector, async () => {
47
47
  const response = await next();
48
48
  const body = response.body;
49
- if (!body) return {
50
- html: null,
51
- status: response.status,
52
- headers: response.headers
53
- };
49
+ const contentType = response.headers.get("content-type") ?? "";
50
+ if (!body || !contentType.includes("text/html")) return { response };
54
51
  const reader = body.pipeThrough(new TextDecoderStream()).getReader();
55
52
  const parts = [];
56
53
  for (;;) {
@@ -64,6 +61,7 @@ function tastyMiddleware(options) {
64
61
  headers: response.headers
65
62
  };
66
63
  });
64
+ if ("response" in rendered) return rendered.response;
67
65
  if (!rendered.html) return new Response(null, {
68
66
  status: rendered.status,
69
67
  headers: rendered.headers
@@ -94,15 +92,26 @@ function tastyMiddleware(options) {
94
92
  });
95
93
  };
96
94
  }
97
- let _middlewareTransferCache = true;
98
- /** @internal */
99
- function setMiddlewareTransferCache(value) {
100
- _middlewareTransferCache = value;
101
- }
102
- /** @internal */
103
- function getMiddlewareTransferCache() {
104
- return _middlewareTransferCache;
105
- }
95
+ /**
96
+ * Package subpaths of the middleware entrypoints registered by
97
+ * `tastyIntegration()`.
98
+ *
99
+ * These must be bare specifiers rather than
100
+ * `new URL('./astro-middleware.js', import.meta.url)`. The bundler is free to
101
+ * hoist `tastyIntegration` into a shared chunk at a different directory depth
102
+ * than `dist/ssr/`, which makes a relative URL resolve to a file that does not
103
+ * exist and breaks the build for every consumer. A package subpath is resolved
104
+ * by the consumer through our `exports` map, so it never depends on the
105
+ * chunk layout.
106
+ *
107
+ * There are two entrypoints instead of one parameterised entrypoint because
108
+ * `addMiddleware()` cannot pass options: the integration runs when the Astro
109
+ * config is loaded, while the middleware module is evaluated in the server
110
+ * runtime — a different process for built output — so module-level state set
111
+ * by the integration is not visible to the middleware.
112
+ */
113
+ const MIDDLEWARE_ENTRYPOINT = "@tenphi/tasty/ssr/astro-middleware";
114
+ const MIDDLEWARE_ENTRYPOINT_STATIC = "@tenphi/tasty/ssr/astro-middleware-static";
106
115
  /**
107
116
  * Astro integration that automatically sets up Tasty SSR.
108
117
  *
@@ -131,12 +140,11 @@ function getMiddlewareTransferCache() {
131
140
  */
132
141
  function tastyIntegration(options) {
133
142
  const { islands = true } = options ?? {};
134
- setMiddlewareTransferCache(islands);
135
143
  return {
136
144
  name: "@tenphi/tasty",
137
145
  hooks: { "astro:config:setup": ({ addMiddleware, injectScript }) => {
138
146
  addMiddleware({
139
- entrypoint: new URL("./astro-middleware.js", import.meta.url),
147
+ entrypoint: islands ? MIDDLEWARE_ENTRYPOINT : MIDDLEWARE_ENTRYPOINT_STATIC,
140
148
  order: "pre"
141
149
  });
142
150
  if (islands) injectScript("before-hydration", `import "@tenphi/tasty/ssr/astro-client";`);
@@ -144,6 +152,6 @@ function tastyIntegration(options) {
144
152
  };
145
153
  }
146
154
  //#endregion
147
- export { getMiddlewareTransferCache, setMiddlewareTransferCache, tastyIntegration, tastyMiddleware };
155
+ export { tastyIntegration, tastyMiddleware };
148
156
 
149
157
  //# sourceMappingURL=astro.js.map
@@ -1 +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"}
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 type Rendered =\n | { response: Response }\n | { html: string | null; status: number; headers: Headers };\n\n const rendered = await runWithCollector<Promise<Rendered>>(\n collector,\n async (): Promise<Rendered> => {\n const response = await next();\n const body = response.body;\n\n // Only process HTML responses. Reading a non-HTML body (e.g. an\n // image, font, or JSON endpoint) as UTF-8 text corrupts binary\n // payloads: every byte >= 0x80 is decoded to U+FFFD and re-encoded\n // as EF BF BD. Pass anything that isn't HTML straight through.\n const contentType = response.headers.get('content-type') ?? '';\n if (!body || !contentType.includes('text/html')) {\n return { response };\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\n // Non-HTML responses are returned untouched to avoid corrupting\n // binary payloads.\n if ('response' in rendered) {\n return rendered.response;\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// Astro Integration API\n// ============================================================================\n\n/**\n * Package subpaths of the middleware entrypoints registered by\n * `tastyIntegration()`.\n *\n * These must be bare specifiers rather than\n * `new URL('./astro-middleware.js', import.meta.url)`. The bundler is free to\n * hoist `tastyIntegration` into a shared chunk at a different directory depth\n * than `dist/ssr/`, which makes a relative URL resolve to a file that does not\n * exist and breaks the build for every consumer. A package subpath is resolved\n * by the consumer through our `exports` map, so it never depends on the\n * chunk layout.\n *\n * There are two entrypoints instead of one parameterised entrypoint because\n * `addMiddleware()` cannot pass options: the integration runs when the Astro\n * config is loaded, while the middleware module is evaluated in the server\n * runtime — a different process for built output — so module-level state set\n * by the integration is not visible to the middleware.\n */\nconst MIDDLEWARE_ENTRYPOINT = '@tenphi/tasty/ssr/astro-middleware';\nconst MIDDLEWARE_ENTRYPOINT_STATIC =\n '@tenphi/tasty/ssr/astro-middleware-static';\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 name: string;\n hooks: Record<string, (...args: never[]) => void>;\n} {\n const { islands = true } = options ?? {};\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: (\n stage: 'head-inline' | 'before-hydration' | 'page' | 'page-ssr',\n content: string,\n ) => void;\n }) => {\n addMiddleware({\n entrypoint: islands\n ? MIDDLEWARE_ENTRYPOINT\n : MIDDLEWARE_ENTRYPOINT_STATIC,\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,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsChD,SAAgB,gBAAgB,SAAkC;CAChE,OAAO,OACL,UACA,SACsB;EACtB,MAAM,gBAAgB,SAAS,iBAAiB;EAChD,MAAM,YAAY,IAAI,qBAAqB;EAS3C,MAAM,WAAW,MAAM,iBACrB,WACA,YAA+B;GAC7B,MAAM,WAAW,MAAM,KAAK;GAC5B,MAAM,OAAO,SAAS;GAMtB,MAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;GAC5D,IAAI,CAAC,QAAQ,CAAC,YAAY,SAAS,WAAW,GAC5C,OAAO,EAAE,SAAS;GAGpB,MAAM,SAAS,KAAK,YAAY,IAAI,kBAAkB,CAAC,CAAC,CAAC,UAAU;GACnE,MAAM,QAAkB,CAAC;GACzB,SAAS;IACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;IAC1C,IAAI,MAAM;IACV,MAAM,KAAK,KAAK;GAClB;GACA,OAAO;IACL,MAAM,MAAM,KAAK,EAAE;IACnB,QAAQ,SAAS;IACjB,SAAS,SAAS;GACpB;EACF,CACF;EAIA,IAAI,cAAc,UAChB,OAAO,SAAS;EAGlB,IAAI,CAAC,SAAS,MACZ,OAAO,IAAI,SAAS,MAAM;GACxB,QAAQ,SAAS;GACjB,SAAS,SAAS;EACpB,CAAC;EAGH,IAAI,EAAE,SAAS;EAEf,MAAM,MAAM,UAAU,OAAO;EAC7B,IAAI,CAAC,KACH,OAAO,IAAI,SAAS,MAAM;GACxB,QAAQ,SAAS;GACjB,SAAS,SAAS;EACpB,CAAC;EAGH,MAAM,QAAQ,UAAU,CAAC,CAAC;EAC1B,MAAM,YAAY,QAAQ,WAAW,MAAM,KAAK;EAChD,MAAM,WAAW,wBAAwB,UAAU,GAAG,IAAI;EAE1D,IAAI,WAAW;EACf,IAAI,eAAe;GACjB,MAAM,aAAa,UAAU,sBAAsB;GACnD,IAAI,WAAW,SAAS,GAEtB,WAAW,UAAU,UAAU,gDADT,WAAW,KAAK,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,GACgC,EAAE;EAEjG;EAEA,MAAM,YAAY,WAAW;EAC7B,MAAM,MAAM,KAAK,QAAQ,SAAS;EAClC,IAAI,QAAQ,IACV,OAAO,KAAK,MAAM,GAAG,GAAG,IAAI,YAAY,KAAK,MAAM,GAAG;OAEtD,OAAO,YAAY;EAGrB,MAAM,UAAU,IAAI,QAAQ,SAAS,OAAO;EAC5C,QAAQ,OAAO,gBAAgB;EAE/B,OAAO,IAAI,SAAS,MAAM;GACxB,QAAQ,SAAS;GACjB;EACF,CAAC;CACH;AACF;;;;;;;;;;;;;;;;;;;AAwBA,MAAM,wBAAwB;AAC9B,MAAM,+BACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CF,SAAgB,iBAAiB,SAG/B;CACA,MAAM,EAAE,UAAU,SAAS,WAAW,CAAC;CAEvC,OAAO;EACL,MAAM;EACN,OAAO,EACL,uBAAuB,EACrB,eACA,mBAUI;GACJ,cAAc;IACZ,YAAY,UACR,wBACA;IACJ,OAAO;GACT,CAAC;GAED,IAAI,SACF,aACE,oBACA,0CACF;EAEJ,EACF;CACF;AACF"}
@@ -1,4 +1,37 @@
1
- import { ServerStyleCollector } from "./collector.js";
2
- import { getSSRCollector, runWithCollector } from "./async-storage.js";
3
- import { hydrateTastyCache, hydrateTastyClasses } from "./hydrate.js";
4
- export { ServerStyleCollector, getSSRCollector, hydrateTastyCache, hydrateTastyClasses, runWithCollector };
1
+ import { n as createServerStyleCollector, t as ServerStyleCollector } from "../collector-DzeNxHOq.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
+ //#region src/ssr/hydrate.d.ts
17
+ /**
18
+ * Client-side cache hydration for SSR/RSC.
19
+ *
20
+ * Pre-populates the client injector's rules map with class names
21
+ * rendered on the server. With hash-based naming, the client derives
22
+ * the same class name from the same cache key, so only the class name
23
+ * list needs to cross the wire — no cache keys or counters.
24
+ */
25
+ /**
26
+ * Pre-populate the client-side style registry from the server's class name list.
27
+ *
28
+ * Call this before ReactDOM.hydrateRoot() or ensure it runs before
29
+ * any tasty() component renders on the client.
30
+ *
31
+ * When called without arguments, reads the class list from `window.__TASTY__`
32
+ * (populated by inline scripts emitted during SSR/RSC streaming).
33
+ */
34
+ declare function hydrateTastyClasses(classes?: string[]): void;
35
+ //#endregion
36
+ export { ServerStyleCollector, createServerStyleCollector, getSSRCollector, hydrateTastyClasses, runWithCollector };
37
+ //# sourceMappingURL=index.d.ts.map
package/dist/ssr/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { registerSSRCollectorGetterGlobal } from "./ssr-collector-ref.js";
2
- import { ServerStyleCollector } from "./collector.js";
3
- import { getSSRCollector, runWithCollector } from "./async-storage.js";
4
- import { hydrateTastyCache, hydrateTastyClasses } from "./hydrate.js";
1
+ import { a as registerSSRCollectorGetterGlobal } from "../format-rules-BkstDvlN.js";
2
+ import { n as createServerStyleCollector, t as ServerStyleCollector } from "../collector-BjkQEtbh.js";
3
+ import { n as runWithCollector, t as getSSRCollector } from "../async-storage-DKK-wTD4.js";
4
+ import { t as hydrateTastyClasses } from "../hydrate-DXXWZgm0.js";
5
5
  //#region src/ssr/index.ts
6
6
  registerSSRCollectorGetterGlobal(getSSRCollector);
7
7
  //#endregion
8
- export { ServerStyleCollector, getSSRCollector, hydrateTastyCache, hydrateTastyClasses, runWithCollector };
8
+ export { ServerStyleCollector, createServerStyleCollector, getSSRCollector, hydrateTastyClasses, runWithCollector };
9
9
 
10
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/ssr/index.ts"],"sourcesContent":["/**\n * SSR entry point for @tenphi/tasty.\n *\n * Provides the core SSR infrastructure: ServerStyleCollector,\n * AsyncLocalStorage integration, and cache hydration.\n *\n * Import from '@tenphi/tasty/ssr'.\n */\n\n// Core collector\nexport { ServerStyleCollector } from './collector';\n\n// AsyncLocalStorage integration for Astro / generic frameworks\nexport { runWithCollector, getSSRCollector } from './async-storage';\n\n// Client-side cache hydration\nexport { hydrateTastyClasses, hydrateTastyCache } from './hydrate';\n\n// Register the ALS getter so hooks can find the collector\n// without importing 'node:async_hooks' in the browser bundle.\n// Uses globalThis so the getter is visible across separate module graphs.\nimport { getSSRCollector } from './async-storage';\nimport { registerSSRCollectorGetterGlobal } from './ssr-collector-ref';\n\nregisterSSRCollectorGetterGlobal(getSSRCollector);\n"],"mappings":";;;;;AAwBA,iCAAiC,gBAAgB"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/ssr/index.ts"],"sourcesContent":["/**\n * SSR entry point for @tenphi/tasty.\n *\n * Provides the core SSR infrastructure: ServerStyleCollector,\n * AsyncLocalStorage integration, and cache hydration.\n *\n * Import from '@tenphi/tasty/ssr'.\n */\n\n// Core collector\nexport { ServerStyleCollector, createServerStyleCollector } from './collector';\n\n// AsyncLocalStorage integration for Astro / generic frameworks\nexport { runWithCollector, getSSRCollector } from './async-storage';\n\n// Client-side cache hydration\nexport { hydrateTastyClasses } from './hydrate';\n\n// Register the ALS getter so hooks can find the collector\n// without importing 'node:async_hooks' in the browser bundle.\n// Uses globalThis so the getter is visible across separate module graphs.\nimport { getSSRCollector } from './async-storage';\nimport { registerSSRCollectorGetterGlobal } from './ssr-collector-ref';\n\nregisterSSRCollectorGetterGlobal(getSSRCollector);\n"],"mappings":";;;;;AAwBA,iCAAiC,eAAe"}
@@ -1,5 +1,4 @@
1
- import { ServerStyleCollector } from "./collector.js";
2
- import * as _$react from "react";
1
+ import { t as ServerStyleCollector } from "../collector-DzeNxHOq.js";
3
2
  import { ReactNode } from "react";
4
3
 
5
4
  //#region src/ssr/next.d.ts
@@ -40,7 +39,7 @@ interface TastyRegistryProps {
40
39
  declare function TastyRegistry({
41
40
  children,
42
41
  transferCache
43
- }: TastyRegistryProps): _$react.FunctionComponentElement<_$react.ProviderProps<ServerStyleCollector | null>>;
42
+ }: TastyRegistryProps): import("react").FunctionComponentElement<import("react").ProviderProps<ServerStyleCollector | null>>;
44
43
  //#endregion
45
44
  export { TastyRegistry, TastyRegistryProps };
46
45
  //# sourceMappingURL=next.d.ts.map
package/dist/ssr/next.js CHANGED
@@ -1,17 +1,16 @@
1
1
  "use client";
2
- import { getConfig } from "../config.js";
3
- import { registerSSRCollectorGetter } from "./ssr-collector-ref.js";
4
- import { TastySSRContext } from "./context.js";
5
- import { ServerStyleCollector } from "./collector.js";
6
- import { hydrateTastyClasses } from "./hydrate.js";
2
+ import { n as getConfig } from "../config-DX126v_w.js";
3
+ import { i as registerSSRCollectorGetter } from "../format-rules-BkstDvlN.js";
4
+ import { t as getTastySSRContext } from "../context-CA8YKeMn.js";
5
+ import { t as ServerStyleCollector } from "../collector-BjkQEtbh.js";
6
+ import { t as hydrateTastyClasses } from "../hydrate-DXXWZgm0.js";
7
7
  import { Fragment, createElement, useState } from "react";
8
8
  import { useServerInsertedHTML } from "next/navigation";
9
9
  //#region src/ssr/next.ts
10
10
  /**
11
11
  * Next.js integration for Tasty SSR.
12
12
  *
13
- * Provides TastyRegistry for App Router (streaming via useServerInsertedHTML)
14
- * and createTastySSRDocument for Pages Router (non-streaming).
13
+ * Provides TastyRegistry for App Router (streaming via useServerInsertedHTML).
15
14
  *
16
15
  * Import from '@tenphi/tasty/ssr/next'.
17
16
  */
@@ -67,7 +66,7 @@ function TastyRegistry({ children, transferCache = true }) {
67
66
  dangerouslySetInnerHTML: { __html: `(window.__TASTY__=window.__TASTY__||[]).push(${classNames.map((n) => `"${n}"`).join(",")})` }
68
67
  }));
69
68
  });
70
- return createElement(TastySSRContext.Provider, { value: collector }, children);
69
+ return createElement(getTastySSRContext().Provider, { value: collector }, children);
71
70
  }
72
71
  //#endregion
73
72
  export { TastyRegistry };
@@ -1 +1 @@
1
- {"version":3,"file":"next.js","names":[],"sources":["../../src/ssr/next.ts"],"sourcesContent":["/**\n * Next.js integration for Tasty SSR.\n *\n * Provides TastyRegistry for App Router (streaming via useServerInsertedHTML)\n * and createTastySSRDocument for Pages Router (non-streaming).\n *\n * Import from '@tenphi/tasty/ssr/next'.\n */\n\n'use client';\n\n/// <reference path=\"./next-navigation.d.ts\" />\n\nimport { createElement, Fragment, useState, type ReactNode } from 'react';\nimport { useServerInsertedHTML } from 'next/navigation';\n\nimport { getConfig } from '../config';\nimport { ServerStyleCollector } from './collector';\nimport { TastySSRContext } from './context';\nimport { hydrateTastyClasses } from './hydrate';\nimport { registerSSRCollectorGetter } from './ssr-collector-ref';\n\n// Auto-hydrate on module load (client only).\n// Reads the class name list from `window.__TASTY__` populated by streaming scripts.\nif (typeof window !== 'undefined') {\n hydrateTastyClasses();\n}\n\nexport interface TastyRegistryProps {\n children: ReactNode;\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 * Next.js App Router registry for Tasty SSR.\n *\n * Wraps the component tree with a ServerStyleCollector and flushes\n * collected CSS into the HTML stream via useServerInsertedHTML.\n *\n * @example\n * ```tsx\n * // app/tasty-registry.tsx\n * 'use client';\n * import { TastyRegistry } from '@tenphi/tasty/ssr/next';\n * export default function TastyStyleRegistry({ children }) {\n * return <TastyRegistry>{children}</TastyRegistry>;\n * }\n *\n * // app/layout.tsx\n * import TastyStyleRegistry from './tasty-registry';\n * export default function RootLayout({ children }) {\n * return <html><body>\n * <TastyStyleRegistry>{children}</TastyStyleRegistry>\n * </body></html>;\n * }\n * ```\n */\nexport function TastyRegistry({\n children,\n transferCache = true,\n}: TastyRegistryProps) {\n const isClient = typeof window !== 'undefined';\n\n const [collector] = useState(() => {\n if (isClient) return null;\n\n const instance = new ServerStyleCollector();\n\n registerSSRCollectorGetter(() => instance);\n\n return instance;\n });\n const nonce = getConfig().nonce;\n\n useServerInsertedHTML(() => {\n if (!collector) return null;\n\n const css = collector.flushCSS();\n const classNames = collector.getRenderedClassNames();\n\n if (!css) return null;\n\n const styleEl = createElement('style', {\n key: 'tasty-ssr-styles',\n 'data-tasty-ssr': '',\n nonce,\n dangerouslySetInnerHTML: { __html: css },\n });\n\n if (!transferCache || classNames.length === 0) return styleEl;\n\n const classListJSON = classNames.map((n) => `\"${n}\"`).join(',');\n\n const scriptEl = createElement('script', {\n key: 'tasty-ssr-cache',\n nonce,\n dangerouslySetInnerHTML: {\n __html: `(window.__TASTY__=window.__TASTY__||[]).push(${classListJSON})`,\n },\n });\n\n return createElement(Fragment, null, styleEl, scriptEl);\n });\n\n return createElement(\n TastySSRContext.Provider,\n { value: collector },\n children,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAwBA,IAAI,OAAO,WAAW,YACpB,sBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;AAsCvB,SAAgB,cAAc,EAC5B,UACA,gBAAgB,QACK;CACrB,MAAM,WAAW,OAAO,WAAW;CAEnC,MAAM,CAAC,aAAa,eAAe;AACjC,MAAI,SAAU,QAAO;EAErB,MAAM,WAAW,IAAI,sBAAsB;AAE3C,mCAAiC,SAAS;AAE1C,SAAO;GACP;CACF,MAAM,QAAQ,WAAW,CAAC;AAE1B,6BAA4B;AAC1B,MAAI,CAAC,UAAW,QAAO;EAEvB,MAAM,MAAM,UAAU,UAAU;EAChC,MAAM,aAAa,UAAU,uBAAuB;AAEpD,MAAI,CAAC,IAAK,QAAO;EAEjB,MAAM,UAAU,cAAc,SAAS;GACrC,KAAK;GACL,kBAAkB;GAClB;GACA,yBAAyB,EAAE,QAAQ,KAAK;GACzC,CAAC;AAEF,MAAI,CAAC,iBAAiB,WAAW,WAAW,EAAG,QAAO;AAYtD,SAAO,cAAc,UAAU,MAAM,SARpB,cAAc,UAAU;GACvC,KAAK;GACL;GACA,yBAAyB,EACvB,QAAQ,gDANU,WAAW,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,IAAI,CAMW,IACvE;GACF,CAAC,CAEqD;GACvD;AAEF,QAAO,cACL,gBAAgB,UAChB,EAAE,OAAO,WAAW,EACpB,SACD"}
1
+ {"version":3,"file":"next.js","names":[],"sources":["../../src/ssr/next.ts"],"sourcesContent":["/**\n * Next.js integration for Tasty SSR.\n *\n * Provides TastyRegistry for App Router (streaming via useServerInsertedHTML).\n *\n * Import from '@tenphi/tasty/ssr/next'.\n */\n\n'use client';\n\n/// <reference path=\"./next-navigation.d.ts\" />\n\nimport { createElement, Fragment, useState, type ReactNode } from 'react';\nimport { useServerInsertedHTML } from 'next/navigation';\n\nimport { getConfig } from '../config';\nimport { ServerStyleCollector } from './collector';\nimport { getTastySSRContext } from './context';\nimport { hydrateTastyClasses } from './hydrate';\nimport { registerSSRCollectorGetter } from './ssr-collector-ref';\n\n// Auto-hydrate on module load (client only).\n// Reads the class name list from `window.__TASTY__` populated by streaming scripts.\nif (typeof window !== 'undefined') {\n hydrateTastyClasses();\n}\n\nexport interface TastyRegistryProps {\n children: ReactNode;\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 * Next.js App Router registry for Tasty SSR.\n *\n * Wraps the component tree with a ServerStyleCollector and flushes\n * collected CSS into the HTML stream via useServerInsertedHTML.\n *\n * @example\n * ```tsx\n * // app/tasty-registry.tsx\n * 'use client';\n * import { TastyRegistry } from '@tenphi/tasty/ssr/next';\n * export default function TastyStyleRegistry({ children }) {\n * return <TastyRegistry>{children}</TastyRegistry>;\n * }\n *\n * // app/layout.tsx\n * import TastyStyleRegistry from './tasty-registry';\n * export default function RootLayout({ children }) {\n * return <html><body>\n * <TastyStyleRegistry>{children}</TastyStyleRegistry>\n * </body></html>;\n * }\n * ```\n */\nexport function TastyRegistry({\n children,\n transferCache = true,\n}: TastyRegistryProps) {\n const isClient = typeof window !== 'undefined';\n\n const [collector] = useState(() => {\n if (isClient) return null;\n\n const instance = new ServerStyleCollector();\n\n registerSSRCollectorGetter(() => instance);\n\n return instance;\n });\n const nonce = getConfig().nonce;\n\n useServerInsertedHTML(() => {\n if (!collector) return null;\n\n const css = collector.flushCSS();\n const classNames = collector.getRenderedClassNames();\n\n if (!css) return null;\n\n const styleEl = createElement('style', {\n key: 'tasty-ssr-styles',\n 'data-tasty-ssr': '',\n nonce,\n dangerouslySetInnerHTML: { __html: css },\n });\n\n if (!transferCache || classNames.length === 0) return styleEl;\n\n const classListJSON = classNames.map((n) => `\"${n}\"`).join(',');\n\n const scriptEl = createElement('script', {\n key: 'tasty-ssr-cache',\n nonce,\n dangerouslySetInnerHTML: {\n __html: `(window.__TASTY__=window.__TASTY__||[]).push(${classListJSON})`,\n },\n });\n\n return createElement(Fragment, null, styleEl, scriptEl);\n });\n\n return createElement(\n getTastySSRContext().Provider,\n { value: collector },\n children,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAuBA,IAAI,OAAO,WAAW,aACpB,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;AAsCtB,SAAgB,cAAc,EAC5B,UACA,gBAAgB,QACK;CACrB,MAAM,WAAW,OAAO,WAAW;CAEnC,MAAM,CAAC,aAAa,eAAe;EACjC,IAAI,UAAU,OAAO;EAErB,MAAM,WAAW,IAAI,qBAAqB;EAE1C,iCAAiC,QAAQ;EAEzC,OAAO;CACT,CAAC;CACD,MAAM,QAAQ,UAAU,CAAC,CAAC;CAE1B,4BAA4B;EAC1B,IAAI,CAAC,WAAW,OAAO;EAEvB,MAAM,MAAM,UAAU,SAAS;EAC/B,MAAM,aAAa,UAAU,sBAAsB;EAEnD,IAAI,CAAC,KAAK,OAAO;EAEjB,MAAM,UAAU,cAAc,SAAS;GACrC,KAAK;GACL,kBAAkB;GAClB;GACA,yBAAyB,EAAE,QAAQ,IAAI;EACzC,CAAC;EAED,IAAI,CAAC,iBAAiB,WAAW,WAAW,GAAG,OAAO;EAYtD,OAAO,cAAc,UAAU,MAAM,SARpB,cAAc,UAAU;GACvC,KAAK;GACL;GACA,yBAAyB,EACvB,QAAQ,gDANU,WAAW,KAAK,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,GAMa,EAAE,GACxE;EACF,CAEqD,CAAC;CACxD,CAAC;CAED,OAAO,cACL,mBAAmB,CAAC,CAAC,UACrB,EAAE,OAAO,UAAU,GACnB,QACF;AACF"}
@@ -1,5 +1,91 @@
1
- import { Styles } from "../styles/types.js";
2
- import { mergeStyles } from "../utils/merge-styles.js";
3
- import { StaticStyle, createStaticStyle, isStaticStyle } from "./types.js";
4
- import { tastyStatic } from "./tastyStatic.js";
5
- export { type StaticStyle, type Styles, createStaticStyle, isStaticStyle, mergeStyles, tastyStatic };
1
+ import { b as Styles } from "../index-BcsaHo8x.js";
2
+ import { t as mergeStyles } from "../merge-styles-BTFAkYSD.js";
3
+
4
+ //#region src/static/types.d.ts
5
+ /**
6
+ * Static style definition returned by tastyStatic().
7
+ *
8
+ * Supports both explicit className access and implicit string coercion via toString().
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const button = tastyStatic({ fill: '#blue' });
13
+ *
14
+ * // Both work in JSX:
15
+ * <div className={button} /> // Uses toString()
16
+ * <div className={button.className} /> // Explicit
17
+ *
18
+ * // Extension:
19
+ * const primary = tastyStatic(button, { fill: '#purple' });
20
+ * ```
21
+ */
22
+ interface StaticStyle {
23
+ /**
24
+ * Generated className(s) for use in JSX.
25
+ * May contain multiple space-separated class names due to chunking.
26
+ */
27
+ className: string;
28
+ /**
29
+ * The original (or merged) styles object.
30
+ * Available for extension via tastyStatic(base, overrides).
31
+ */
32
+ styles: Styles;
33
+ /**
34
+ * Returns className for implicit string coercion.
35
+ * Enables `<div className={button} />` syntax.
36
+ */
37
+ toString(): string;
38
+ }
39
+ /**
40
+ * Create a StaticStyle object.
41
+ * Used internally by the Babel plugin to generate output.
42
+ */
43
+ declare function createStaticStyle(className: string, styles: Styles): StaticStyle;
44
+ /**
45
+ * Type guard to check if a value is a StaticStyle object.
46
+ */
47
+ declare function isStaticStyle(value: unknown): value is StaticStyle;
48
+ //#endregion
49
+ //#region src/static/tastyStatic.d.ts
50
+ /**
51
+ * Generate styles and return a StaticStyle object.
52
+ * The object has `className`, `styles`, and `toString()`.
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const button = tastyStatic({
57
+ * fill: '#blue',
58
+ * padding: '2x',
59
+ * });
60
+ * // After build: { className: 'ts3f2a1b', styles: {...}, toString() }
61
+ *
62
+ * <div className={button} /> // Works via toString()
63
+ * ```
64
+ */
65
+ declare function tastyStatic(styles: Styles): StaticStyle;
66
+ /**
67
+ * Extend an existing StaticStyle with additional styles.
68
+ * Uses mergeStyles() internally for proper nested selector handling.
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const button = tastyStatic({ fill: '#blue' });
73
+ * const primary = tastyStatic(button, { fill: '#purple' });
74
+ * // After build: { className: 'ts8c4d2e', styles: {...merged...}, toString() }
75
+ * ```
76
+ */
77
+ declare function tastyStatic(base: StaticStyle, styles: Styles): StaticStyle;
78
+ /**
79
+ * Generate styles for a specific CSS selector.
80
+ * The call is completely removed after build transformation.
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * tastyStatic('.heading', { preset: 'h1', color: '#primary' });
85
+ * // After build: (removed)
86
+ * ```
87
+ */
88
+ declare function tastyStatic(selector: string, styles: Styles): void;
89
+ //#endregion
90
+ export { type StaticStyle, type Styles, createStaticStyle, isStaticStyle, mergeStyles, tastyStatic };
91
+ //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,50 @@
1
- import { mergeStyles } from "../utils/merge-styles.js";
2
- import { createStaticStyle, isStaticStyle } from "./types.js";
3
- import { tastyStatic } from "./tastyStatic.js";
1
+ import { t as mergeStyles } from "../merge-styles-Dpueyn82.js";
2
+ //#region src/static/types.ts
3
+ /**
4
+ * Create a StaticStyle object.
5
+ * Used internally by the Babel plugin to generate output.
6
+ */
7
+ function createStaticStyle(className, styles) {
8
+ return {
9
+ className,
10
+ styles,
11
+ toString() {
12
+ return this.className;
13
+ }
14
+ };
15
+ }
16
+ /**
17
+ * Type guard to check if a value is a StaticStyle object.
18
+ */
19
+ function isStaticStyle(value) {
20
+ return typeof value === "object" && value !== null && "className" in value && "styles" in value && "toString" in value && typeof value.className === "string" && typeof value.styles === "object";
21
+ }
22
+ //#endregion
23
+ //#region src/static/tastyStatic.ts
24
+ /**
25
+ * Build-time only function for zero-runtime static site generation.
26
+ *
27
+ * This function is transformed by the Babel plugin:
28
+ * - `tastyStatic(styles)` → StaticStyle object with className
29
+ * - `tastyStatic(base, styles)` → StaticStyle object with merged styles
30
+ * - `tastyStatic(selector, styles)` → removed entirely
31
+ *
32
+ * At runtime (during development/build), this function returns a placeholder.
33
+ * In production, all calls are replaced/removed by the build plugin.
34
+ */
35
+ function tastyStatic(stylesOrBaseOrSelector, styles) {
36
+ if (typeof stylesOrBaseOrSelector === "string") {
37
+ console.warn(`[Tasty] tastyStatic('${stylesOrBaseOrSelector}', styles) was called at runtime. This indicates the Babel plugin is not configured. Add @tenphi/tasty/babel-plugin to your Babel config.`);
38
+ return;
39
+ }
40
+ if (isStaticStyle(stylesOrBaseOrSelector)) {
41
+ console.warn("[Tasty] tastyStatic(base, styles) was called at runtime. This indicates the Babel plugin is not configured. Add @tenphi/tasty/babel-plugin to your Babel config.");
42
+ return createStaticStyle("__TASTY_STATIC_NOT_TRANSFORMED__", mergeStyles(stylesOrBaseOrSelector.styles, styles || {}));
43
+ }
44
+ console.warn("[Tasty] tastyStatic(styles) was called at runtime. This indicates the Babel plugin is not configured. Add @tenphi/tasty/babel-plugin to your Babel config.");
45
+ return createStaticStyle("__TASTY_STATIC_NOT_TRANSFORMED__", stylesOrBaseOrSelector);
46
+ }
47
+ //#endregion
4
48
  export { createStaticStyle, isStaticStyle, mergeStyles, tastyStatic };
49
+
50
+ //# sourceMappingURL=index.js.map