@lovrozagar/flare 0.1.0

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 (387) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/package.json +146 -0
  4. package/scripts/download-fonts.ts +136 -0
  5. package/scripts/populate-fonts.ts +721 -0
  6. package/src/await.ts +2 -0
  7. package/src/boundaries/index.ts +99 -0
  8. package/src/broadcast/channel.ts +65 -0
  9. package/src/broadcast/context.ts +12 -0
  10. package/src/broadcast/index.ts +75 -0
  11. package/src/broadcast/provider.tsx +7 -0
  12. package/src/build-location.ts +2 -0
  13. package/src/caches/index.ts +384 -0
  14. package/src/client/index.ts +75 -0
  15. package/src/client.tsx +2 -0
  16. package/src/codegen/index.ts +41 -0
  17. package/src/components/await.tsx +140 -0
  18. package/src/components/dev-error-overlay.tsx +154 -0
  19. package/src/components/direction-script.tsx +22 -0
  20. package/src/components/index.ts +150 -0
  21. package/src/components/locale-script.tsx +26 -0
  22. package/src/components/reset-css.tsx +14 -0
  23. package/src/components/ssr-context.tsx +37 -0
  24. package/src/components/theme-script.tsx +22 -0
  25. package/src/components/view-transition-css.tsx +21 -0
  26. package/src/config/index.ts +84 -0
  27. package/src/create-client-only-fn.ts +1 -0
  28. package/src/create-isomorphic-fn.ts +2 -0
  29. package/src/create-layout.ts +1 -0
  30. package/src/create-page.ts +1 -0
  31. package/src/create-path-segment.ts +1 -0
  32. package/src/create-query-client-getter.ts +2 -0
  33. package/src/create-root-layout.ts +1 -0
  34. package/src/create-router.ts +8 -0
  35. package/src/create-server-only-fn.ts +1 -0
  36. package/src/dedupe/index.ts +151 -0
  37. package/src/defer/index.ts +82 -0
  38. package/src/direction/index.tsx +163 -0
  39. package/src/direction.ts +3 -0
  40. package/src/duration/index.ts +18 -0
  41. package/src/env-fn/index.ts +101 -0
  42. package/src/env-fn.ts +2 -0
  43. package/src/errors/index.ts +166 -0
  44. package/src/fonts/abeezee.ts +50 -0
  45. package/src/fonts/abel.ts +26 -0
  46. package/src/fonts/abril-fatface.ts +34 -0
  47. package/src/fonts/acme.ts +26 -0
  48. package/src/fonts/advent-pro.ts +92 -0
  49. package/src/fonts/alegreya-sans.ts +228 -0
  50. package/src/fonts/alegreya.ts +228 -0
  51. package/src/fonts/alex-brush.ts +42 -0
  52. package/src/fonts/alfa-slab-one.ts +42 -0
  53. package/src/fonts/alice.ts +48 -0
  54. package/src/fonts/amatic-sc.ts +94 -0
  55. package/src/fonts/amiri.ts +114 -0
  56. package/src/fonts/antic-slab.ts +26 -0
  57. package/src/fonts/anton.ts +42 -0
  58. package/src/fonts/archivo-black.ts +34 -0
  59. package/src/fonts/archivo-narrow.ts +114 -0
  60. package/src/fonts/archivo.ts +66 -0
  61. package/src/fonts/arimo.ts +256 -0
  62. package/src/fonts/arvo.ts +50 -0
  63. package/src/fonts/asap.ts +66 -0
  64. package/src/fonts/assistant.ts +64 -0
  65. package/src/fonts/baloo-2.ts +82 -0
  66. package/src/fonts/barlow-condensed.ts +114 -0
  67. package/src/fonts/barlow-semi-condensed.ts +114 -0
  68. package/src/fonts/barlow.ts +114 -0
  69. package/src/fonts/be-vietnam-pro.ts +114 -0
  70. package/src/fonts/bebas-neue.ts +34 -0
  71. package/src/fonts/besley.ts +82 -0
  72. package/src/fonts/biorhyme.ts +50 -0
  73. package/src/fonts/bitter.ts +94 -0
  74. package/src/fonts/black-ops-one.ts +49 -0
  75. package/src/fonts/bree-serif.ts +34 -0
  76. package/src/fonts/bricolage-grotesque.ts +66 -0
  77. package/src/fonts/cabin.ts +114 -0
  78. package/src/fonts/cairo.ts +66 -0
  79. package/src/fonts/cardo.ts +193 -0
  80. package/src/fonts/catamaran.ts +41 -0
  81. package/src/fonts/caveat.ts +78 -0
  82. package/src/fonts/chakra-petch.ts +142 -0
  83. package/src/fonts/chivo.ts +66 -0
  84. package/src/fonts/cinzel.ts +50 -0
  85. package/src/fonts/comfortaa.ts +109 -0
  86. package/src/fonts/commissioner.ts +64 -0
  87. package/src/fonts/component.tsx +27 -0
  88. package/src/fonts/cormorant-garamond.ts +171 -0
  89. package/src/fonts/cormorant.ts +170 -0
  90. package/src/fonts/courgette.ts +34 -0
  91. package/src/fonts/courier-prime.ts +82 -0
  92. package/src/fonts/create-font.ts +47 -0
  93. package/src/fonts/create-registry-font.ts +40 -0
  94. package/src/fonts/crete-round.ts +50 -0
  95. package/src/fonts/crimson-text.ts +114 -0
  96. package/src/fonts/css.ts +86 -0
  97. package/src/fonts/dancing-script.ts +66 -0
  98. package/src/fonts/dm-mono.ts +50 -0
  99. package/src/fonts/dm-sans.ts +50 -0
  100. package/src/fonts/dm-serif-display.ts +50 -0
  101. package/src/fonts/domine.ts +50 -0
  102. package/src/fonts/dosis.ts +66 -0
  103. package/src/fonts/eb-garamond.ts +228 -0
  104. package/src/fonts/eczar.ts +94 -0
  105. package/src/fonts/encode-sans.ts +42 -0
  106. package/src/fonts/epilogue.ts +66 -0
  107. package/src/fonts/exo-2.ts +94 -0
  108. package/src/fonts/exo.ts +66 -0
  109. package/src/fonts/figtree.ts +82 -0
  110. package/src/fonts/fira-code.ts +121 -0
  111. package/src/fonts/fira-sans-condensed.ts +228 -0
  112. package/src/fonts/fira-sans.ts +228 -0
  113. package/src/fonts/fjalla-one.ts +49 -0
  114. package/src/fonts/font-urls.gen.json +1962 -0
  115. package/src/fonts/fraunces.ts +66 -0
  116. package/src/fonts/fredoka.ts +64 -0
  117. package/src/fonts/gelasio.ts +114 -0
  118. package/src/fonts/gloria-hallelujah.ts +34 -0
  119. package/src/fonts/gothic-a1.ts +124 -0
  120. package/src/fonts/great-vibes.ts +64 -0
  121. package/src/fonts/gudea.ts +66 -0
  122. package/src/fonts/heebo.ts +57 -0
  123. package/src/fonts/hind-siliguri.ts +66 -0
  124. package/src/fonts/hind.ts +66 -0
  125. package/src/fonts/ibm-plex-mono.ts +172 -0
  126. package/src/fonts/ibm-plex-sans-arabic.ts +80 -0
  127. package/src/fonts/ibm-plex-sans.ts +199 -0
  128. package/src/fonts/ibm-plex-serif.ts +171 -0
  129. package/src/fonts/inconsolata.ts +66 -0
  130. package/src/fonts/index.ts +4 -0
  131. package/src/fonts/indie-flower.ts +34 -0
  132. package/src/fonts/instrument-sans.ts +82 -0
  133. package/src/fonts/instrument-serif.ts +50 -0
  134. package/src/fonts/inter-display.ts +52 -0
  135. package/src/fonts/inter-tight.ts +124 -0
  136. package/src/fonts/inter.ts +123 -0
  137. package/src/fonts/jetbrains-mono.ts +199 -0
  138. package/src/fonts/josefin-sans.ts +114 -0
  139. package/src/fonts/josefin-slab.ts +50 -0
  140. package/src/fonts/jost.ts +64 -0
  141. package/src/fonts/kalam.ts +66 -0
  142. package/src/fonts/kanit.ts +142 -0
  143. package/src/fonts/karla.ts +82 -0
  144. package/src/fonts/khand.ts +66 -0
  145. package/src/fonts/lato.ts +82 -0
  146. package/src/fonts/league-spartan.ts +42 -0
  147. package/src/fonts/lexend-deca.ts +42 -0
  148. package/src/fonts/lexend.ts +42 -0
  149. package/src/fonts/libre-baskerville.ts +82 -0
  150. package/src/fonts/libre-franklin.ts +95 -0
  151. package/src/fonts/lilita-one.ts +34 -0
  152. package/src/fonts/lobster-two.ts +50 -0
  153. package/src/fonts/lobster.ts +56 -0
  154. package/src/fonts/lora.ts +235 -0
  155. package/src/fonts/m-plus-rounded-1c.ts +138 -0
  156. package/src/fonts/manrope.ts +109 -0
  157. package/src/fonts/maven-pro.ts +66 -0
  158. package/src/fonts/merriweather-sans.ts +142 -0
  159. package/src/fonts/merriweather.ts +171 -0
  160. package/src/fonts/montserrat-alternates.ts +171 -0
  161. package/src/fonts/montserrat.ts +94 -0
  162. package/src/fonts/mukta.ts +66 -0
  163. package/src/fonts/mulish.ts +170 -0
  164. package/src/fonts/nanum-gothic.ts +34 -0
  165. package/src/fonts/nanum-myeongjo.ts +34 -0
  166. package/src/fonts/neuton.ts +66 -0
  167. package/src/fonts/newsreader.ts +114 -0
  168. package/src/fonts/noticia-text.ts +114 -0
  169. package/src/fonts/noto-sans-arabic.ts +58 -0
  170. package/src/fonts/noto-sans-hk.ts +49 -0
  171. package/src/fonts/noto-sans-jp.ts +49 -0
  172. package/src/fonts/noto-sans-kr.ts +49 -0
  173. package/src/fonts/noto-sans-sc.ts +49 -0
  174. package/src/fonts/noto-sans-tc.ts +49 -0
  175. package/src/fonts/noto-sans.ts +140 -0
  176. package/src/fonts/noto-serif-jp.ts +80 -0
  177. package/src/fonts/noto-serif.ts +140 -0
  178. package/src/fonts/nunito-sans.ts +170 -0
  179. package/src/fonts/nunito.ts +170 -0
  180. package/src/fonts/old-standard-tt.ts +133 -0
  181. package/src/fonts/onest.ts +48 -0
  182. package/src/fonts/open-sans.ts +340 -0
  183. package/src/fonts/orbitron.ts +34 -0
  184. package/src/fonts/oswald.ts +94 -0
  185. package/src/fonts/outfit.ts +34 -0
  186. package/src/fonts/overpass.ts +94 -0
  187. package/src/fonts/oxygen.ts +50 -0
  188. package/src/fonts/pacifico.ts +56 -0
  189. package/src/fonts/passion-one.ts +50 -0
  190. package/src/fonts/pathway-gothic-one.ts +34 -0
  191. package/src/fonts/permanent-marker.ts +26 -0
  192. package/src/fonts/philosopher.ts +172 -0
  193. package/src/fonts/play.ts +109 -0
  194. package/src/fonts/playfair-display.ts +142 -0
  195. package/src/fonts/plus-jakarta-sans.ts +142 -0
  196. package/src/fonts/poppins.ts +114 -0
  197. package/src/fonts/prompt.ts +142 -0
  198. package/src/fonts/pt-sans-narrow.ts +78 -0
  199. package/src/fonts/pt-sans.ts +138 -0
  200. package/src/fonts/pt-serif.ts +138 -0
  201. package/src/fonts/public-sans.ts +66 -0
  202. package/src/fonts/questrial.ts +42 -0
  203. package/src/fonts/quicksand.ts +66 -0
  204. package/src/fonts/rajdhani.ts +66 -0
  205. package/src/fonts/raleway.ts +94 -0
  206. package/src/fonts/recursive.ts +80 -0
  207. package/src/fonts/red-hat-display.ts +82 -0
  208. package/src/fonts/red-rose.ts +66 -0
  209. package/src/fonts/registry.gen.ts +173 -0
  210. package/src/fonts/righteous.ts +34 -0
  211. package/src/fonts/roboto-condensed.ts +124 -0
  212. package/src/fonts/roboto-flex.ts +64 -0
  213. package/src/fonts/roboto-mono.ts +199 -0
  214. package/src/fonts/roboto-slab.ts +72 -0
  215. package/src/fonts/roboto.ts +156 -0
  216. package/src/fonts/rokkitt.ts +66 -0
  217. package/src/fonts/rubik.ts +199 -0
  218. package/src/fonts/sacramento.ts +34 -0
  219. package/src/fonts/saira.ts +66 -0
  220. package/src/fonts/sarabun.ts +142 -0
  221. package/src/fonts/satisfy.ts +26 -0
  222. package/src/fonts/sawarabi-mincho.ts +41 -0
  223. package/src/fonts/shadows-into-light.ts +34 -0
  224. package/src/fonts/signika-negative.ts +66 -0
  225. package/src/fonts/signika.ts +66 -0
  226. package/src/fonts/slabo-27px.ts +34 -0
  227. package/src/fonts/sora.ts +50 -0
  228. package/src/fonts/source-code-pro.ts +228 -0
  229. package/src/fonts/source-sans-3.ts +228 -0
  230. package/src/fonts/source-serif-4.ts +199 -0
  231. package/src/fonts/space-grotesk.ts +66 -0
  232. package/src/fonts/space-mono.ts +114 -0
  233. package/src/fonts/spectral.ts +170 -0
  234. package/src/fonts/tajawal.ts +50 -0
  235. package/src/fonts/teko.ts +66 -0
  236. package/src/fonts/tenor-sans.ts +41 -0
  237. package/src/fonts/tinos.ts +256 -0
  238. package/src/fonts/titillium-web.ts +82 -0
  239. package/src/fonts/types.ts +43 -0
  240. package/src/fonts/ubuntu-condensed.ts +63 -0
  241. package/src/fonts/ubuntu-mono.ts +195 -0
  242. package/src/fonts/ubuntu.ts +195 -0
  243. package/src/fonts/unbounded.ts +94 -0
  244. package/src/fonts/unicode-ranges.gen.ts +28 -0
  245. package/src/fonts/urbanist.ts +50 -0
  246. package/src/fonts/varela-round.ts +49 -0
  247. package/src/fonts/vollkorn.ts +199 -0
  248. package/src/fonts/work-sans.ts +66 -0
  249. package/src/fonts/yanone-kaffeesatz.ts +128 -0
  250. package/src/fonts/yantramanav.ts +66 -0
  251. package/src/fonts/yellowtail.ts +34 -0
  252. package/src/fonts/young-serif.ts +34 -0
  253. package/src/fonts/zen-kaku-gothic-new.ts +64 -0
  254. package/src/fonts/zilla-slab.ts +82 -0
  255. package/src/form/index.tsx +205 -0
  256. package/src/generators/index.ts +1589 -0
  257. package/src/head-client/index.ts +649 -0
  258. package/src/history/index.ts +210 -0
  259. package/src/hooks.ts +61 -0
  260. package/src/hydrate/index.tsx +381 -0
  261. package/src/hydration/index.ts +187 -0
  262. package/src/i18n/index.ts +250 -0
  263. package/src/i18n/jsx.tsx +42 -0
  264. package/src/i18n/message-format.ts +174 -0
  265. package/src/image/index.tsx +473 -0
  266. package/src/image.ts +12 -0
  267. package/src/intercept-outlet/index.tsx +18 -0
  268. package/src/intercept-outlet.ts +2 -0
  269. package/src/internal/index.ts +143 -0
  270. package/src/internal/once-idle.ts +24 -0
  271. package/src/internal.ts +12 -0
  272. package/src/lazy/index.tsx +205 -0
  273. package/src/link/index.tsx +367 -0
  274. package/src/loader-pipeline/index.ts +639 -0
  275. package/src/locale/index.tsx +137 -0
  276. package/src/locale.ts +3 -0
  277. package/src/logger.ts +22 -0
  278. package/src/middleware/builtins/api-proxy.ts +64 -0
  279. package/src/middleware/builtins/cdn-proxy.ts +58 -0
  280. package/src/middleware/builtins/i18n.ts +256 -0
  281. package/src/middleware/builtins/keepalive.ts +24 -0
  282. package/src/middleware/builtins/markdown-negotiation.ts +141 -0
  283. package/src/middleware/builtins/static-assets.ts +32 -0
  284. package/src/middleware/index.ts +223 -0
  285. package/src/module-graph/index.ts +181 -0
  286. package/src/mount/index.ts +146 -0
  287. package/src/navigate.ts +1 -0
  288. package/src/navigation/index.ts +1278 -0
  289. package/src/navigation/types.ts +59 -0
  290. package/src/navigation-progress/index.tsx +247 -0
  291. package/src/navigation-progress.ts +1 -0
  292. package/src/ndjson-client/index.ts +376 -0
  293. package/src/ndjson-server/index.ts +284 -0
  294. package/src/outlet/index.tsx +633 -0
  295. package/src/outlet/types.ts +243 -0
  296. package/src/plugins/css-transform.ts +70 -0
  297. package/src/plugins/dev-cdn-cache.ts +454 -0
  298. package/src/plugins/dev-dashboard/client.tsx +2603 -0
  299. package/src/plugins/dev-dashboard/match-route-tree.ts +159 -0
  300. package/src/plugins/dev-dashboard/plugin.ts +404 -0
  301. package/src/plugins/dev-prerender.ts +168 -0
  302. package/src/plugins/dev-server.ts +327 -0
  303. package/src/plugins/generate-watch.ts +32 -0
  304. package/src/plugins/image-plugin.ts +171 -0
  305. package/src/plugins/index.ts +445 -0
  306. package/src/plugins/options.ts +37 -0
  307. package/src/plugins/parse-utils.ts +59 -0
  308. package/src/plugins/prerender-plugin.ts +144 -0
  309. package/src/plugins/purge.ts +242 -0
  310. package/src/plugins/server-fn.ts +641 -0
  311. package/src/plugins/service-worker.ts +225 -0
  312. package/src/plugins/solid-jsx.d.ts +12 -0
  313. package/src/plugins/sx-ast/atomic.ts +166 -0
  314. package/src/plugins/sx-ast/evaluator.ts +235 -0
  315. package/src/plugins/sx-ast/index.ts +372 -0
  316. package/src/plugins/sx-ast/parser.ts +22 -0
  317. package/src/plugins/sx-ast/rewrite.ts +763 -0
  318. package/src/plugins/sx.d.ts +14 -0
  319. package/src/plugins/tw-compile.ts +240 -0
  320. package/src/plugins/types.ts +41 -0
  321. package/src/plugins/virtual.d.ts +110 -0
  322. package/src/plugins/virtual.ts +184 -0
  323. package/src/preload/index.ts +77 -0
  324. package/src/prerender/index.ts +431 -0
  325. package/src/query-client/index.tsx +168 -0
  326. package/src/registry/index.ts +88 -0
  327. package/src/reset-css.ts +1 -0
  328. package/src/revalidation/index.ts +84 -0
  329. package/src/rewrite/index.ts +83 -0
  330. package/src/route-builder/create-layout.ts +655 -0
  331. package/src/route-builder/create-page.ts +806 -0
  332. package/src/route-builder/create-path-segment.ts +34 -0
  333. package/src/route-builder/create-root-layout.ts +608 -0
  334. package/src/route-builder/index.ts +49 -0
  335. package/src/route-builder/register.ts +242 -0
  336. package/src/route-builder/types.ts +294 -0
  337. package/src/router-config/index.ts +91 -0
  338. package/src/router-primitives/index.ts +25 -0
  339. package/src/router-primitives/location.ts +29 -0
  340. package/src/router-primitives/match-id.ts +111 -0
  341. package/src/router-primitives/paths.ts +109 -0
  342. package/src/router-primitives/tree.ts +243 -0
  343. package/src/router-primitives/types.ts +74 -0
  344. package/src/router.ts +31 -0
  345. package/src/search-engine/bing.ts +32 -0
  346. package/src/search-engine/google-jwt.ts +95 -0
  347. package/src/search-engine/google.ts +139 -0
  348. package/src/search-engine/index-now.ts +64 -0
  349. package/src/search-engine/index.ts +12 -0
  350. package/src/security/index.ts +230 -0
  351. package/src/server/index.ts +286 -0
  352. package/src/server-context/browser.ts +113 -0
  353. package/src/server-context/index.ts +217 -0
  354. package/src/server-fn/index.ts +710 -0
  355. package/src/server-fn-query.ts +169 -0
  356. package/src/server-handler/etag.ts +51 -0
  357. package/src/server-handler/headers.ts +5 -0
  358. package/src/server-handler/index.ts +1664 -0
  359. package/src/server-handler/types.ts +24 -0
  360. package/src/server-handler/validate-static-params.ts +130 -0
  361. package/src/server.ts +2 -0
  362. package/src/service-worker/template.ts +153 -0
  363. package/src/service-worker-hook/index.ts +70 -0
  364. package/src/sitemap/index.ts +231 -0
  365. package/src/ssr/critical-css.ts +150 -0
  366. package/src/ssr/head-prefix.ts +65 -0
  367. package/src/ssr/head.ts +259 -0
  368. package/src/ssr/index.tsx +879 -0
  369. package/src/state-parser/index.ts +266 -0
  370. package/src/store/filesystem.ts +131 -0
  371. package/src/store/index.ts +29 -0
  372. package/src/styles/index.ts +1200 -0
  373. package/src/styles/sx-types.ts +11 -0
  374. package/src/testing/index.ts +251 -0
  375. package/src/testing.ts +2 -0
  376. package/src/theme/index.tsx +232 -0
  377. package/src/theme.ts +3 -0
  378. package/src/tracing/index.ts +4 -0
  379. package/src/tracing/noop.ts +13 -0
  380. package/src/tracing/otel.ts +37 -0
  381. package/src/tracing/timing.ts +34 -0
  382. package/src/tracing/types.ts +9 -0
  383. package/src/url/index.ts +140 -0
  384. package/src/use-router.ts +11 -0
  385. package/src/use-suspense-query.ts +2 -0
  386. package/src/validation/index.ts +57 -0
  387. package/src/view-transition-css.ts +1 -0
@@ -0,0 +1,159 @@
1
+ import { toLocaleMatch, type LocaleMatch } from "../../router-primitives/tree.ts";
2
+
3
+ export interface MatchTreeNode {
4
+ children: MatchTreeNode[];
5
+ segment: string;
6
+ }
7
+
8
+ export interface MatchedTreeRoute<T extends MatchTreeNode = MatchTreeNode> {
9
+ node: T;
10
+ params: Record<string, string>;
11
+ }
12
+
13
+ function localeAllows(paramName: string, value: string, localeMatch?: LocaleMatch): boolean {
14
+ return !localeMatch || paramName !== localeMatch.paramName || localeMatch.locales.includes(value);
15
+ }
16
+
17
+ function optionalCatchAllName(seg: string): string | null {
18
+ const m = /^\[\[\.\.\.(\w+)\]\]$/.exec(seg);
19
+ return m?.[1] ?? null;
20
+ }
21
+
22
+ function catchAllName(seg: string): string | null {
23
+ const m = /^\[\.\.\.(\w+)\]$/.exec(seg);
24
+ return m?.[1] ?? null;
25
+ }
26
+
27
+ function optionalParamName(seg: string): string | null {
28
+ const m = /^\[\[(\w+)\]\]$/.exec(seg);
29
+ return m?.[1] ?? null;
30
+ }
31
+
32
+ function requiredParamName(seg: string): string | null {
33
+ const m = /^\[(\w+)\]$/.exec(seg);
34
+ return m?.[1] ?? null;
35
+ }
36
+
37
+ /**
38
+ * Inspector matcher for the dashboard route tree.
39
+ * Same locale allow-list as `matchRoute`: `[locale]` / `[[locale]]` only
40
+ * consume declared locale values when `localeMatch` is set.
41
+ */
42
+ export function matchRouteTree<T extends MatchTreeNode>(
43
+ nodes: T[],
44
+ pathname: string,
45
+ localeMatch?: LocaleMatch,
46
+ ): { chain: MatchedTreeRoute<T>[]; params: Record<string, string> } {
47
+ const urlSegments = pathname.split("/").filter(Boolean);
48
+ const params: Record<string, string> = {};
49
+ const chain: MatchedTreeRoute<T>[] = [];
50
+
51
+ function walk(children: T[], idx: number): boolean {
52
+ for (const node of children) {
53
+ const seg = node.segment;
54
+ const isGroup = seg.startsWith("(") && seg.endsWith(")");
55
+ const optCatch = optionalCatchAllName(seg);
56
+ const catchAll = catchAllName(seg);
57
+ const optionalName = optionalParamName(seg);
58
+ const requiredName = requiredParamName(seg);
59
+
60
+ if (isGroup) {
61
+ chain.push({ node, params: {} });
62
+ if (walk(node.children as T[], idx)) return true;
63
+ chain.pop();
64
+ continue;
65
+ }
66
+
67
+ if (optCatch) {
68
+ const remaining = urlSegments.slice(idx);
69
+ params[optCatch] = remaining.join("/");
70
+ chain.push({ node, params: { [optCatch]: remaining.join("/") } });
71
+ return true;
72
+ }
73
+
74
+ if (catchAll) {
75
+ if (idx >= urlSegments.length) continue;
76
+ const remaining = urlSegments.slice(idx);
77
+ params[catchAll] = remaining.join("/");
78
+ chain.push({ node, params: { [catchAll]: remaining.join("/") } });
79
+ return true;
80
+ }
81
+
82
+ if (optionalName) {
83
+ /* skip first — static siblings / descendants win */
84
+ chain.push({ node, params: {} });
85
+ if (walk(node.children as T[], idx)) return true;
86
+ chain.pop();
87
+
88
+ const currentSeg = urlSegments[idx];
89
+ if (currentSeg !== undefined && localeAllows(optionalName, currentSeg, localeMatch)) {
90
+ params[optionalName] = currentSeg;
91
+ chain.push({ node, params: { [optionalName]: currentSeg } });
92
+ if (walk(node.children as T[], idx + 1)) return true;
93
+ if (idx + 1 === urlSegments.length) return true;
94
+ delete params[optionalName];
95
+ chain.pop();
96
+ }
97
+ continue;
98
+ }
99
+
100
+ const currentSeg = urlSegments[idx];
101
+
102
+ if (requiredName) {
103
+ if (idx >= urlSegments.length) continue;
104
+ if (!localeAllows(requiredName, currentSeg ?? "", localeMatch)) continue;
105
+ params[requiredName] = currentSeg ?? "";
106
+ chain.push({ node, params: { [requiredName]: currentSeg ?? "" } });
107
+ if (walk(node.children as T[], idx + 1)) return true;
108
+ if (idx + 1 === urlSegments.length) return true;
109
+ delete params[requiredName];
110
+ chain.pop();
111
+ continue;
112
+ }
113
+
114
+ if (seg === currentSeg || (seg === "/" && idx === urlSegments.length)) {
115
+ chain.push({ node, params: {} });
116
+ if (seg === "/" && idx === urlSegments.length) return true;
117
+ if (walk(node.children as T[], idx + 1)) return true;
118
+ if (idx + 1 === urlSegments.length) return true;
119
+ chain.pop();
120
+ }
121
+ }
122
+ return false;
123
+ }
124
+
125
+ if (nodes.length > 0) {
126
+ const root = nodes[0];
127
+ if (root) {
128
+ chain.push({ node: root, params: {} });
129
+ walk(root.children as T[], 0);
130
+ }
131
+ }
132
+
133
+ return { chain, params };
134
+ }
135
+
136
+ function readLocaleCfg(value: unknown): { locales: readonly string[]; paramName?: string } | undefined {
137
+ if (!value || typeof value !== "object") return undefined;
138
+ const locales = (value as { locales?: unknown }).locales;
139
+ if (!Array.isArray(locales) || locales.some((l) => typeof l !== "string")) return undefined;
140
+ const paramName = (value as { paramName?: unknown }).paramName;
141
+ return {
142
+ locales: locales as string[],
143
+ paramName: typeof paramName === "string" ? paramName : undefined,
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Pull a `LocaleMatch` from a router module: named `localeConfig`, then
149
+ * `router.locale` / `default.locale`.
150
+ */
151
+ export function extractLocaleMatchFromModule(mod: Record<string, unknown>): LocaleMatch | undefined {
152
+ const named = toLocaleMatch(readLocaleCfg(mod.localeConfig));
153
+ if (named) return named;
154
+ const router = (mod.router ?? mod.default) as Record<string, unknown> | undefined;
155
+ if (router && typeof router === "object") {
156
+ return toLocaleMatch(readLocaleCfg(router.locale));
157
+ }
158
+ return undefined;
159
+ }
@@ -0,0 +1,404 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import type { RouteDefinition } from "../../generators/index.ts";
5
+ import { scanSourceFiles } from "../../generators/index.ts";
6
+ import type { LocaleMatch } from "../../router-primitives/tree.ts";
7
+ import type { FlarePluginConfig, VitePlugin } from "../index.ts";
8
+ import { extractLocaleMatchFromModule } from "./match-route-tree.ts";
9
+
10
+ /* ── Types ───────────────────────────────────────────────────────────── */
11
+
12
+ export interface RouteTreeNode {
13
+ auth: false | "optional" | true;
14
+ cache: RouteDefinition["cache"];
15
+ children: RouteTreeNode[];
16
+ filePath: string | null;
17
+ hasInput: boolean;
18
+ intercept?: RouteDefinition["intercept"];
19
+ responseRoute: boolean;
20
+ segment: string;
21
+ type: RouteDefinition["type"] | "segment";
22
+ urlPath: string;
23
+ virtualPath: string | null;
24
+ }
25
+
26
+ interface ServerFnInfo {
27
+ authenticate: boolean;
28
+ file: string;
29
+ id: string;
30
+ method: string;
31
+ name: string;
32
+ stream: boolean;
33
+ }
34
+
35
+ /* ── Route tree builder ──────────────────────────────────────────────── */
36
+
37
+ function virtualPathToUrlPath(virtualPath: string): string {
38
+ const parts = virtualPath.split("/");
39
+ const rootIdx = parts.findIndex((p) => p.startsWith("_") && p.endsWith("_") && p.length >= 3);
40
+ const urlParts: string[] = [];
41
+
42
+ for (let i = 0; i < parts.length; i++) {
43
+ const part = parts[i] ?? "";
44
+ if (rootIdx >= 0 && i <= rootIdx) continue;
45
+ if (part.startsWith("(") && part.endsWith(")")) continue;
46
+ if (part === "") continue;
47
+ urlParts.push(part);
48
+ }
49
+
50
+ return `/${urlParts.join("/")}`;
51
+ }
52
+
53
+ function virtualPathToSegments(virtualPath: string): string[] {
54
+ const parts = virtualPath.split("/");
55
+ const rootIdx = parts.findIndex((p) => p.startsWith("_") && p.endsWith("_") && p.length >= 3);
56
+ const segments: string[] = [];
57
+
58
+ for (let i = 0; i < parts.length; i++) {
59
+ const part = parts[i] ?? "";
60
+ if (rootIdx >= 0 && i <= rootIdx) continue;
61
+ if (part === "") continue;
62
+ segments.push(part);
63
+ }
64
+
65
+ return segments;
66
+ }
67
+
68
+ function emptyNode(segment: string, urlPath: string): RouteTreeNode {
69
+ return {
70
+ auth: false,
71
+ cache: {},
72
+ children: [],
73
+ filePath: null,
74
+ hasInput: false,
75
+ responseRoute: false,
76
+ segment,
77
+ type: "segment",
78
+ urlPath,
79
+ virtualPath: null,
80
+ };
81
+ }
82
+
83
+ function applyDef(node: RouteTreeNode, def: RouteDefinition, preserveType?: boolean): void {
84
+ node.auth = def.authenticateMode;
85
+ node.cache = def.cache;
86
+ node.filePath = def.filePath;
87
+ node.hasInput = def.hasInput;
88
+ node.intercept = def.intercept;
89
+ node.responseRoute = def.responseRoute;
90
+ node.urlPath = virtualPathToUrlPath(def.virtualPath);
91
+ node.virtualPath = def.virtualPath;
92
+ if (preserveType) {
93
+ if (node.type === "segment") node.type = def.type;
94
+ } else {
95
+ node.type = def.type;
96
+ }
97
+ }
98
+
99
+ export function buildRouteTree(defs: RouteDefinition[]): RouteTreeNode[] {
100
+ const root = emptyNode("", "/");
101
+
102
+ for (const def of defs) {
103
+ if (def.type === "root-layout") {
104
+ applyDef(root, def);
105
+ continue;
106
+ }
107
+
108
+ const segments = virtualPathToSegments(def.virtualPath);
109
+
110
+ if (segments.length === 0) {
111
+ const idx = emptyNode("/", "/");
112
+ applyDef(idx, def);
113
+ root.children.push(idx);
114
+ continue;
115
+ }
116
+
117
+ let current = root;
118
+ let accUrl = "";
119
+
120
+ for (const seg of segments) {
121
+ const isGroup = seg.startsWith("(") && seg.endsWith(")");
122
+ if (!isGroup) accUrl += `/${seg}`;
123
+
124
+ let child = current.children.find((c) => c.segment === seg);
125
+ if (!child) {
126
+ child = emptyNode(seg, isGroup ? current.urlPath : accUrl || "/");
127
+ current.children.push(child);
128
+ }
129
+
130
+ current = child;
131
+ }
132
+
133
+ applyDef(current, def, def.type === "layout");
134
+ }
135
+
136
+ function sortTree(node: RouteTreeNode): void {
137
+ node.children.sort((a, b) => a.segment.localeCompare(b.segment));
138
+ for (const child of node.children) sortTree(child);
139
+ }
140
+ sortTree(root);
141
+
142
+ return root.children.length > 0 || root.type !== "segment" ? [root] : [];
143
+ }
144
+
145
+ /* ── Data collection ─────────────────────────────────────────────────── */
146
+
147
+ function collectRouteDefs(root: string, ignorePrefix: string): RouteDefinition[] {
148
+ return scanSourceFiles({ ignorePrefix, rootDir: root });
149
+ }
150
+
151
+ function collectRouteTree(root: string, ignorePrefix: string): RouteTreeNode[] {
152
+ const defs = scanSourceFiles({ ignorePrefix, rootDir: root });
153
+ return buildRouteTree(defs);
154
+ }
155
+
156
+ async function collectServerFns(
157
+ ssrRunner: { import: (id: string) => Promise<Record<string, unknown>> } | undefined,
158
+ ): Promise<ServerFnInfo[]> {
159
+ if (!ssrRunner) return [];
160
+
161
+ try {
162
+ const mod = await ssrRunner.import("virtual:flare-server-fn-map");
163
+ const map = (mod.default ?? mod) as Map<string, Record<string, unknown>> | undefined;
164
+ if (!map || typeof map.entries !== "function") return [];
165
+
166
+ const fns: ServerFnInfo[] = [];
167
+ const seen = new Set<string>();
168
+
169
+ for (const [, reg] of map) {
170
+ const id = String(reg.id ?? "");
171
+ if (seen.has(id)) continue;
172
+ seen.add(id);
173
+
174
+ fns.push({
175
+ authenticate: Boolean(reg.authenticate),
176
+ file: String(reg.file ?? ""),
177
+ id,
178
+ method: String(reg.method ?? "post"),
179
+ name: String(reg.name ?? id),
180
+ stream: Boolean(reg.stream),
181
+ });
182
+ }
183
+
184
+ return fns.sort((a, b) => a.name.localeCompare(b.name));
185
+ } catch {
186
+ return [];
187
+ }
188
+ }
189
+
190
+ const ROUTER_CANDIDATES = ["src/router.ts", "src/router.tsx"] as const;
191
+
192
+ export async function collectLocaleMatch(
193
+ ssrRunner: { import: (id: string) => Promise<Record<string, unknown>> } | undefined,
194
+ root: string,
195
+ ): Promise<LocaleMatch | undefined> {
196
+ if (!ssrRunner) return undefined;
197
+
198
+ for (const rel of ROUTER_CANDIDATES) {
199
+ if (!existsSync(join(root, rel))) continue;
200
+ try {
201
+ const mod = await ssrRunner.import(`./${rel}`);
202
+ const match = extractLocaleMatchFromModule(mod);
203
+ if (match) return match;
204
+ } catch {
205
+ /* try next candidate */
206
+ }
207
+ }
208
+ return undefined;
209
+ }
210
+
211
+ /* ── Builder chain extraction ─────────────────────────────────────────── */
212
+
213
+ const BUILDER_METHODS = new Set([
214
+ "authenticate",
215
+ "authenticateOptional",
216
+ "authorize",
217
+ "cache",
218
+ "effects",
219
+ "errorRender",
220
+ "head",
221
+ "headers",
222
+ "input",
223
+ "intercept",
224
+ "loader",
225
+ "notFoundRender",
226
+ "preloader",
227
+ "render",
228
+ "response",
229
+ "unauthenticatedRender",
230
+ "unauthorizedRender",
231
+ ]);
232
+
233
+ function extractBuilderChains(root: string, defs: RouteDefinition[]): Record<string, string[]> {
234
+ const result: Record<string, string[]> = {};
235
+
236
+ for (const def of defs) {
237
+ try {
238
+ const content = readFileSync(join(root, def.filePath), "utf-8");
239
+ const methods: string[] = [];
240
+ const seen = new Set<string>();
241
+
242
+ const re = /\.(\w+)\s*\(/g;
243
+ let match = re.exec(content);
244
+ while (match) {
245
+ const name = match[1] ?? "";
246
+ if (BUILDER_METHODS.has(name) && !seen.has(name)) {
247
+ seen.add(name);
248
+ methods.push(name);
249
+ }
250
+ match = re.exec(content);
251
+ }
252
+
253
+ result[def.filePath] = methods;
254
+ } catch {
255
+ result[def.filePath] = [];
256
+ }
257
+ }
258
+
259
+ return result;
260
+ }
261
+
262
+ /* ── Plugin ──────────────────────────────────────────────────────────── */
263
+
264
+ interface DashboardRes {
265
+ end: (data?: unknown) => void;
266
+ writeHead: (status: number, headers?: Record<string, string>) => void;
267
+ }
268
+
269
+ interface DashboardDevServer {
270
+ config?: { root?: string };
271
+ environments?: Record<string, { runner?: { import: (id: string) => Promise<Record<string, unknown>> } }>;
272
+ middlewares: {
273
+ use: (
274
+ fn: (
275
+ req: {
276
+ headers: Record<string, string | string[] | undefined>;
277
+ method?: string;
278
+ url?: string;
279
+ },
280
+ res: DashboardRes,
281
+ next: () => void,
282
+ ) => void,
283
+ ) => void;
284
+ };
285
+ }
286
+
287
+ const VIRTUAL_ID = "virtual:flare-devtools";
288
+ const RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_ID}`;
289
+
290
+ function resolveClientFilePath(): string {
291
+ try {
292
+ const req = createRequire(import.meta.url);
293
+ const pluginsPath = req.resolve("@lovrozagar/flare/plugins");
294
+ return join(dirname(pluginsPath), "dev-dashboard", "client.tsx").replace(/\\/g, "/");
295
+ } catch {
296
+ /* Fallback: resolve relative to this file */
297
+ return join(dirname(import.meta.url.replace("file://", "")), "client.tsx").replace(/\\/g, "/");
298
+ }
299
+ }
300
+
301
+ export function createDevDashboardPlugin(config: FlarePluginConfig): VitePlugin {
302
+ const ignorePrefix = config.ignorePrefix ?? "_";
303
+ let clientFilePath = "";
304
+
305
+ return {
306
+ buildStart() {
307
+ if (!clientFilePath) {
308
+ clientFilePath = resolveClientFilePath();
309
+ }
310
+ },
311
+ configureServer(server: unknown) {
312
+ const vite = server as DashboardDevServer;
313
+ const root = vite.config?.root ?? process.cwd();
314
+
315
+ vite.middlewares.use(async (req, res, next) => {
316
+ if (!req.url?.startsWith("/__flare/")) return next();
317
+
318
+ if (req.url.startsWith("/__flare/open-editor?")) {
319
+ const params = new URL(req.url, "http://localhost").searchParams;
320
+ const file = params.get("file");
321
+ if (file) {
322
+ const filePath = resolve(join(root, file));
323
+ const rootWithSlash = root.endsWith("/") ? root : `${root}/`;
324
+ if (filePath.startsWith(rootWithSlash)) {
325
+ import("node:child_process")
326
+ .then(({ execFile }) => {
327
+ execFile("code", ["--goto", filePath], () => {});
328
+ })
329
+ .catch(() => {});
330
+ }
331
+ }
332
+ res.writeHead(204);
333
+ res.end();
334
+ return;
335
+ }
336
+
337
+ if (!req.url.startsWith("/__flare/api")) return next();
338
+
339
+ try {
340
+ const ssrRunner = vite.environments?.ssr?.runner;
341
+ const defs = collectRouteDefs(root, ignorePrefix);
342
+ const routeTree = collectRouteTree(root, ignorePrefix);
343
+ const serverFunctions = await collectServerFns(ssrRunner);
344
+ const builderChains = extractBuilderChains(root, defs);
345
+ const localeMatch = await collectLocaleMatch(ssrRunner, root);
346
+
347
+ res.writeHead(200, {
348
+ "access-control-allow-origin": "*",
349
+ "content-type": "application/json",
350
+ });
351
+ res.end(JSON.stringify({ builderChains, defs, localeMatch, routeTree, serverFunctions }, null, 2));
352
+ } catch (e: unknown) {
353
+ const msg = e instanceof Error ? e.message : String(e);
354
+ res.writeHead(500, { "content-type": "application/json" });
355
+ res.end(JSON.stringify({ error: msg }));
356
+ }
357
+ });
358
+
359
+ return undefined;
360
+ },
361
+ enforce: "pre",
362
+ load(id: string): { code: string; moduleType: string } | null {
363
+ if (id !== RESOLVED_VIRTUAL_ID) return null;
364
+
365
+ if (!clientFilePath) {
366
+ clientFilePath = resolveClientFilePath();
367
+ }
368
+
369
+ if (!clientFilePath) {
370
+ return {
371
+ code: "/* flare devtools: unable to resolve client module */",
372
+ moduleType: "js",
373
+ };
374
+ }
375
+
376
+ return {
377
+ code: `import { mount } from "${clientFilePath}"\nmount()`,
378
+ moduleType: "js",
379
+ };
380
+ },
381
+ name: "flare:dev-dashboard",
382
+ resolveId(id: string) {
383
+ if (id === VIRTUAL_ID) return RESOLVED_VIRTUAL_ID;
384
+ return null;
385
+ },
386
+ transformIndexHtml(html: string) {
387
+ const nonceMatch = html.match(/nonce="([^"]*)"/);
388
+ const nonce = nonceMatch?.[1];
389
+
390
+ if (clientFilePath) {
391
+ return [
392
+ {
393
+ attrs: { type: "module", ...(nonce ? { nonce } : {}) },
394
+ children: `import("/@fs${clientFilePath}").then(m => m.mount())`,
395
+ injectTo: "body" as const,
396
+ tag: "script",
397
+ },
398
+ ];
399
+ }
400
+
401
+ return [];
402
+ },
403
+ };
404
+ }