@squadbase/vantage 0.6.1 → 0.6.2-dev1.0017a4d

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 (319) hide show
  1. package/dist/add-4GHF2TX2.js +6 -0
  2. package/dist/add-4GHF2TX2.js.map +1 -0
  3. package/dist/{build-O5TVNH4E.js → build-X5NVCQKT.js} +9 -9
  4. package/dist/{build-O5TVNH4E.js.map → build-X5NVCQKT.js.map} +1 -1
  5. package/dist/{check-WFBEDHDR.js → check-ZBIH422T.js} +7 -7
  6. package/dist/{check-WFBEDHDR.js.map → check-ZBIH422T.js.map} +1 -1
  7. package/dist/chunk-2IAPIOPS.js +152 -0
  8. package/dist/chunk-2IAPIOPS.js.map +1 -0
  9. package/dist/chunk-2ICNHUG6.js +1820 -0
  10. package/dist/chunk-2ICNHUG6.js.map +1 -0
  11. package/dist/{chunk-5ZOP4SQR.js → chunk-2N72FKLB.js} +12 -7
  12. package/dist/chunk-2N72FKLB.js.map +1 -0
  13. package/dist/chunk-57GHR5LN.js +163 -0
  14. package/dist/chunk-57GHR5LN.js.map +1 -0
  15. package/dist/{chunk-BCE22JSM.js → chunk-7DZGJQ3P.js} +33 -14
  16. package/dist/chunk-7DZGJQ3P.js.map +1 -0
  17. package/dist/{chunk-MXI3PZFT.js → chunk-7S52M2SL.js} +257 -210
  18. package/dist/chunk-7S52M2SL.js.map +1 -0
  19. package/dist/{chunk-QDZA5JSL.js → chunk-AABMKHEU.js} +4 -4
  20. package/dist/{chunk-QDZA5JSL.js.map → chunk-AABMKHEU.js.map} +1 -1
  21. package/dist/{chunk-KLEIVDUV.js → chunk-CEXAQQ5W.js} +150 -11
  22. package/dist/chunk-CEXAQQ5W.js.map +1 -0
  23. package/dist/{chunk-3G43C6OL.js → chunk-GNXCX4WE.js} +46 -3
  24. package/dist/chunk-GNXCX4WE.js.map +1 -0
  25. package/dist/{chunk-JGRMRXXZ.js → chunk-JKLLFVEU.js} +3 -3
  26. package/dist/{chunk-JGRMRXXZ.js.map → chunk-JKLLFVEU.js.map} +1 -1
  27. package/dist/chunk-JXFCSF3K.js +673 -0
  28. package/dist/chunk-JXFCSF3K.js.map +1 -0
  29. package/dist/chunk-PDOBHMUQ.js +54 -0
  30. package/dist/chunk-PDOBHMUQ.js.map +1 -0
  31. package/dist/{chunk-ZVANIULZ.js → chunk-RNTDBX6S.js} +4 -4
  32. package/dist/{chunk-ZVANIULZ.js.map → chunk-RNTDBX6S.js.map} +1 -1
  33. package/dist/chunk-RU6WKEOG.js +7048 -0
  34. package/dist/chunk-RU6WKEOG.js.map +1 -0
  35. package/dist/{chunk-A2UUGASH.js → chunk-U736PVHY.js} +2 -2
  36. package/dist/chunk-U736PVHY.js.map +1 -0
  37. package/dist/{chunk-PVTHHV7X.js → chunk-V3LF7BE7.js} +27 -5
  38. package/dist/chunk-V3LF7BE7.js.map +1 -0
  39. package/dist/{chunk-EP6JHXSR.js → chunk-WAMZWZJB.js} +54 -9
  40. package/dist/chunk-WAMZWZJB.js.map +1 -0
  41. package/dist/{chunk-DTDVSFRY.js → chunk-XQUSGIDG.js} +17 -3
  42. package/dist/chunk-XQUSGIDG.js.map +1 -0
  43. package/dist/cli.js +15 -11
  44. package/dist/cli.js.map +1 -1
  45. package/dist/client/index.d.ts +32 -3
  46. package/dist/client/index.js +205 -12
  47. package/dist/client/index.js.map +1 -1
  48. package/dist/components/index.d.ts +1743 -29
  49. package/dist/components/index.js +7 -3
  50. package/dist/{dev-NZRLOQCL.js → dev-4XEDTELV.js} +24 -13
  51. package/dist/dev-4XEDTELV.js.map +1 -0
  52. package/dist/doc/index.d.ts +198 -2
  53. package/dist/doc/index.js +438 -11
  54. package/dist/doc/index.js.map +1 -1
  55. package/dist/{docs-ZGDJUHJI.js → docs-JVP4RPKT.js} +3 -3
  56. package/dist/{docs-ZGDJUHJI.js.map → docs-JVP4RPKT.js.map} +1 -1
  57. package/dist/{doctor-VFKEMR54.js → doctor-5E2VEZPK.js} +8 -8
  58. package/dist/doctor-5E2VEZPK.js.map +1 -0
  59. package/dist/format-U0cQl069.d.ts +243 -0
  60. package/dist/markdown/index.js +1 -1
  61. package/dist/markdown/index.js.map +1 -1
  62. package/dist/{preview-QDCIVVSR.js → preview-T6CLSQVX.js} +12 -4
  63. package/dist/preview-T6CLSQVX.js.map +1 -0
  64. package/dist/query/index.d.ts +0 -11
  65. package/dist/query/index.js +2 -2
  66. package/dist/{routes-RHP5FVGD.js → routes-3SGAFDMC.js} +6 -6
  67. package/dist/{routes-RHP5FVGD.js.map → routes-3SGAFDMC.js.map} +1 -1
  68. package/dist/{search-RFBELGY7.js → search-5LC4XPB5.js} +3 -3
  69. package/dist/{search-RFBELGY7.js.map → search-5LC4XPB5.js.map} +1 -1
  70. package/dist/slide/index.d.ts +177 -1
  71. package/dist/slide/index.js +917 -18
  72. package/dist/slide/index.js.map +1 -1
  73. package/dist/table/index.js +14 -9
  74. package/dist/table/index.js.map +1 -1
  75. package/dist/ui/index.d.ts +148 -12
  76. package/dist/ui/index.js +1340 -87
  77. package/dist/ui/index.js.map +1 -1
  78. package/dist/{upgrade-GF2P67M6.js → upgrade-JHXZBGQS.js} +7 -7
  79. package/dist/{upgrade-GF2P67M6.js.map → upgrade-JHXZBGQS.js.map} +1 -1
  80. package/dist/vite/index.d.ts +10 -0
  81. package/dist/vite/index.js +4 -4
  82. package/docs/en/changelog.md +24 -0
  83. package/docs/en/cli-reference.md +30 -0
  84. package/docs/en/components.md +40 -17
  85. package/docs/en/doc/doc-action-items.md +49 -0
  86. package/docs/en/doc/doc-callout.md +38 -0
  87. package/docs/en/doc/doc-data-table.md +60 -0
  88. package/docs/en/doc/doc-executive-summary.md +64 -0
  89. package/docs/en/doc/doc-figure.md +56 -0
  90. package/docs/en/doc/doc-key-findings.md +61 -0
  91. package/docs/en/doc/doc-kpi-scorecard.md +80 -0
  92. package/docs/en/doc/doc-report-header.md +46 -0
  93. package/docs/en/parts/area-chart.md +99 -0
  94. package/docs/en/parts/bar-chart.md +91 -0
  95. package/docs/en/parts/bar-list.md +50 -0
  96. package/docs/en/parts/category-bar.md +37 -0
  97. package/docs/en/parts/chart-frame.md +39 -0
  98. package/docs/en/parts/data-table.md +22 -0
  99. package/docs/en/parts/delta.md +50 -0
  100. package/docs/en/parts/donut-chart.md +72 -0
  101. package/docs/en/parts/format.md +43 -0
  102. package/docs/en/parts/funnel-steps.md +2 -1
  103. package/docs/en/parts/heatmap.md +53 -0
  104. package/docs/en/parts/kpi-card.md +57 -0
  105. package/docs/en/parts/legend.md +35 -0
  106. package/docs/en/parts/line-chart.md +94 -0
  107. package/docs/en/parts/metric-value.md +3 -2
  108. package/docs/en/parts/page-shell.md +1 -1
  109. package/docs/en/parts/progress-circle.md +33 -0
  110. package/docs/en/parts/sample-data.md +39 -0
  111. package/docs/en/parts/scatter-chart.md +56 -0
  112. package/docs/en/parts/sparkline.md +1 -1
  113. package/docs/en/parts/tracker.md +36 -0
  114. package/docs/en/parts/trend-indicator.md +17 -1
  115. package/docs/en/parts/updated-at.md +30 -0
  116. package/docs/en/parts/waterfall-chart.md +56 -0
  117. package/docs/en/routing.md +4 -2
  118. package/docs/en/slide/slide-chart-insight.md +52 -0
  119. package/docs/en/slide/slide-cover.md +39 -0
  120. package/docs/en/slide/slide-exec-summary.md +47 -0
  121. package/docs/en/slide/slide-kpi-grid.md +55 -0
  122. package/docs/en/slide/slide-kpi-hero.md +58 -0
  123. package/docs/en/slide/slide-next-actions.md +49 -0
  124. package/docs/en/slide/slide-scorecard-table.md +54 -0
  125. package/docs/en/ui/alert-dialog.md +42 -0
  126. package/docs/en/ui/avatar.md +31 -0
  127. package/docs/en/ui/combobox.md +74 -0
  128. package/docs/en/ui/drawer.md +41 -0
  129. package/docs/en/ui/empty.md +1 -1
  130. package/docs/en/ui/error-state.md +4 -3
  131. package/docs/en/ui/field.md +42 -0
  132. package/docs/en/ui/hover-card.md +21 -0
  133. package/docs/en/ui/kbd.md +17 -0
  134. package/docs/en/ui/loading.md +1 -1
  135. package/docs/en/ui/pagination.md +36 -0
  136. package/docs/en/ui/radio-group.md +21 -0
  137. package/docs/en/ui/toast.md +44 -0
  138. package/docs/en/ui/toggle-group.md +4 -3
  139. package/docs/en/ui-and-theming.md +1 -1
  140. package/docs/index.json +1024 -114
  141. package/docs/ja/changelog.md +55 -0
  142. package/docs/ja/cli-reference.md +29 -0
  143. package/docs/ja/components.md +66 -101
  144. package/docs/ja/doc/doc-action-items.md +49 -0
  145. package/docs/ja/doc/doc-callout.md +38 -0
  146. package/docs/ja/doc/doc-data-table.md +60 -0
  147. package/docs/ja/doc/doc-executive-summary.md +64 -0
  148. package/docs/ja/doc/doc-figure.md +56 -0
  149. package/docs/ja/doc/doc-key-findings.md +61 -0
  150. package/docs/ja/doc/doc-kpi-scorecard.md +80 -0
  151. package/docs/ja/doc/doc-report-header.md +46 -0
  152. package/docs/ja/page-types/csv.md +83 -0
  153. package/docs/ja/page-types/doc.md +83 -0
  154. package/docs/ja/page-types/excel.md +82 -0
  155. package/docs/ja/page-types/slide.md +132 -0
  156. package/docs/ja/page-types.md +47 -0
  157. package/docs/ja/parts/area-chart.md +85 -0
  158. package/docs/ja/parts/bar-chart.md +95 -0
  159. package/docs/ja/parts/bar-list.md +58 -0
  160. package/docs/ja/parts/category-bar.md +36 -0
  161. package/docs/ja/parts/chart-frame.md +36 -0
  162. package/docs/ja/parts/data-table.md +22 -0
  163. package/docs/ja/parts/delta.md +50 -0
  164. package/docs/ja/parts/donut-chart.md +66 -0
  165. package/docs/ja/parts/echart.md +3 -0
  166. package/docs/ja/parts/format.md +44 -0
  167. package/docs/ja/parts/funnel-steps.md +27 -8
  168. package/docs/ja/parts/heatmap.md +45 -0
  169. package/docs/ja/parts/kpi-card.md +55 -0
  170. package/docs/ja/parts/legend.md +36 -0
  171. package/docs/ja/parts/line-chart.md +84 -0
  172. package/docs/ja/parts/metric-value.md +3 -2
  173. package/docs/ja/parts/page-shell.md +1 -1
  174. package/docs/ja/parts/progress-circle.md +34 -0
  175. package/docs/ja/parts/sample-data.md +61 -0
  176. package/docs/ja/parts/scatter-chart.md +69 -0
  177. package/docs/ja/parts/sparkline.md +2 -2
  178. package/docs/ja/parts/tracker.md +32 -0
  179. package/docs/ja/parts/trend-indicator.md +17 -1
  180. package/docs/ja/parts/updated-at.md +27 -0
  181. package/docs/ja/parts/waterfall-chart.md +61 -0
  182. package/docs/ja/routing.md +8 -84
  183. package/docs/ja/slide/slide-chart-insight.md +52 -0
  184. package/docs/ja/slide/slide-cover.md +39 -0
  185. package/docs/ja/slide/slide-exec-summary.md +47 -0
  186. package/docs/ja/slide/slide-kpi-grid.md +55 -0
  187. package/docs/ja/slide/slide-kpi-hero.md +58 -0
  188. package/docs/ja/slide/slide-next-actions.md +49 -0
  189. package/docs/ja/slide/slide-scorecard-table.md +54 -0
  190. package/docs/ja/ui/alert-dialog.md +41 -0
  191. package/docs/ja/ui/avatar.md +31 -0
  192. package/docs/ja/ui/combobox.md +74 -0
  193. package/docs/ja/ui/drawer.md +41 -0
  194. package/docs/ja/ui/empty.md +1 -1
  195. package/docs/ja/ui/error-state.md +4 -3
  196. package/docs/ja/ui/field.md +42 -0
  197. package/docs/ja/ui/hover-card.md +21 -0
  198. package/docs/ja/ui/kbd.md +16 -0
  199. package/docs/ja/ui/loading.md +1 -1
  200. package/docs/ja/ui/pagination.md +36 -0
  201. package/docs/ja/ui/radio-group.md +21 -0
  202. package/docs/ja/ui/toast.md +46 -0
  203. package/docs/ja/ui/toggle-group.md +4 -3
  204. package/docs/ja/ui-and-theming.md +14 -7
  205. package/package.json +43 -29
  206. package/registry/ui/accordion.tsx +76 -0
  207. package/registry/ui/alert-dialog.tsx +189 -0
  208. package/registry/ui/alert.tsx +80 -0
  209. package/registry/ui/app-shell.tsx +287 -0
  210. package/registry/ui/area-chart.tsx +3701 -0
  211. package/registry/ui/attention-list.tsx +303 -0
  212. package/registry/ui/avatar.tsx +110 -0
  213. package/registry/ui/badge.tsx +56 -0
  214. package/registry/ui/bar-chart.tsx +3698 -0
  215. package/registry/ui/bar-list.tsx +3743 -0
  216. package/registry/ui/box-plot-chart.tsx +3906 -0
  217. package/registry/ui/breadcrumb.tsx +128 -0
  218. package/registry/ui/button.tsx +62 -0
  219. package/registry/ui/calendar.tsx +223 -0
  220. package/registry/ui/card.tsx +107 -0
  221. package/registry/ui/category-bar.tsx +1290 -0
  222. package/registry/ui/chart-frame.tsx +1393 -0
  223. package/registry/ui/checkbox.tsx +30 -0
  224. package/registry/ui/collapsible.tsx +23 -0
  225. package/registry/ui/combobox.tsx +310 -0
  226. package/registry/ui/command.tsx +198 -0
  227. package/registry/ui/dashboard-card.tsx +217 -0
  228. package/registry/ui/data-table.tsx +693 -146
  229. package/registry/ui/date-range-picker.tsx +390 -0
  230. package/registry/ui/delta.tsx +188 -0
  231. package/registry/ui/dialog.tsx +161 -0
  232. package/registry/ui/donut-chart.tsx +3751 -0
  233. package/registry/ui/drawer.tsx +229 -0
  234. package/registry/ui/dropdown-menu.tsx +274 -0
  235. package/registry/ui/echart.tsx +1040 -0
  236. package/registry/ui/field.tsx +240 -0
  237. package/registry/ui/filter-bar.tsx +392 -0
  238. package/registry/ui/funnel-steps.tsx +3569 -0
  239. package/registry/ui/heatmap.tsx +1786 -0
  240. package/registry/ui/histogram-chart.tsx +3986 -0
  241. package/registry/ui/hover-card.tsx +52 -0
  242. package/registry/ui/input-group.tsx +160 -0
  243. package/registry/ui/input.tsx +24 -0
  244. package/registry/ui/kbd.tsx +30 -0
  245. package/registry/ui/kpi-card.tsx +699 -0
  246. package/registry/ui/label.tsx +22 -0
  247. package/registry/ui/legend.tsx +3563 -0
  248. package/registry/ui/line-chart.tsx +3699 -0
  249. package/registry/ui/metric-value.tsx +62 -0
  250. package/registry/ui/multi-select.tsx +232 -0
  251. package/registry/ui/page-shell.tsx +282 -0
  252. package/registry/ui/pagination.tsx +140 -0
  253. package/registry/ui/paired-chart.tsx +3827 -0
  254. package/registry/ui/placeholder.tsx +36 -0
  255. package/registry/ui/popover.tsx +92 -0
  256. package/registry/ui/progress-circle.tsx +867 -0
  257. package/registry/ui/progress.tsx +85 -0
  258. package/registry/ui/radio-group.tsx +39 -0
  259. package/registry/ui/sample-data.tsx +86 -0
  260. package/registry/ui/scatter-chart.tsx +4212 -0
  261. package/registry/ui/scroll-area.tsx +56 -0
  262. package/registry/ui/searchable-select.tsx +214 -0
  263. package/registry/ui/section-header.tsx +43 -0
  264. package/registry/ui/segmented-control.tsx +76 -0
  265. package/registry/ui/select.tsx +255 -0
  266. package/registry/ui/separator.tsx +27 -0
  267. package/registry/ui/sheet.tsx +139 -0
  268. package/registry/ui/sidebar.tsx +725 -0
  269. package/registry/ui/skeleton.tsx +17 -0
  270. package/registry/ui/slider.tsx +56 -0
  271. package/registry/ui/sparkline.tsx +230 -0
  272. package/registry/ui/spinner.tsx +14 -0
  273. package/registry/ui/states.tsx +90 -0
  274. package/registry/ui/status-badge.tsx +121 -0
  275. package/registry/ui/switch.tsx +34 -0
  276. package/registry/ui/table.tsx +118 -0
  277. package/registry/ui/tabs.tsx +84 -0
  278. package/registry/ui/textarea.tsx +22 -0
  279. package/registry/ui/toast.tsx +250 -0
  280. package/registry/ui/toggle-group.tsx +91 -0
  281. package/registry/ui/toggle.tsx +47 -0
  282. package/registry/ui/tooltip.tsx +72 -0
  283. package/registry/ui/tracker.tsx +849 -0
  284. package/registry/ui/trend-indicator.tsx +74 -0
  285. package/registry/ui/updated-at.tsx +122 -0
  286. package/registry/ui/waterfall-chart.tsx +4248 -0
  287. package/skills/vantage-add-feature/SKILL.md +9 -7
  288. package/skills/vantage-design/SKILL.md +36 -24
  289. package/skills/vantage-design/references/extract.md +16 -14
  290. package/skills/vantage-design/references/format.md +6 -5
  291. package/skills/vantage-design/references/presets.md +60 -110
  292. package/skills/vantage-pitfalls/SKILL.md +5 -1
  293. package/skills/vantage-slide/SKILL.md +71 -36
  294. package/templates/AGENTS.md +24 -3
  295. package/templates/agents/api.md +29 -0
  296. package/templates/agents/csv.md +2 -1
  297. package/templates/agents/doc.md +51 -3
  298. package/templates/agents/page.md +54 -9
  299. package/templates/agents/slide.md +68 -3
  300. package/templates/agents/xlsx.md +2 -1
  301. package/theme.css +284 -128
  302. package/dist/add-EISGSZAH.js +0 -6
  303. package/dist/add-EISGSZAH.js.map +0 -1
  304. package/dist/chunk-3G43C6OL.js.map +0 -1
  305. package/dist/chunk-5ZOP4SQR.js.map +0 -1
  306. package/dist/chunk-A2UUGASH.js.map +0 -1
  307. package/dist/chunk-BCE22JSM.js.map +0 -1
  308. package/dist/chunk-DTDVSFRY.js.map +0 -1
  309. package/dist/chunk-EP6JHXSR.js.map +0 -1
  310. package/dist/chunk-KLEIVDUV.js.map +0 -1
  311. package/dist/chunk-MXI3PZFT.js.map +0 -1
  312. package/dist/chunk-PLST5Q7T.js +0 -2221
  313. package/dist/chunk-PLST5Q7T.js.map +0 -1
  314. package/dist/chunk-PVTHHV7X.js.map +0 -1
  315. package/dist/chunk-T3FLAGI6.js +0 -316
  316. package/dist/chunk-T3FLAGI6.js.map +0 -1
  317. package/dist/dev-NZRLOQCL.js.map +0 -1
  318. package/dist/doctor-VFKEMR54.js.map +0 -1
  319. package/dist/preview-QDCIVVSR.js.map +0 -1
@@ -4,6 +4,119 @@ import fs from 'fs';
4
4
 
5
5
  // @squadbase/vantage — generated build. Do not edit.
6
6
 
7
+ // src/core/placeholder.ts
8
+ var PLACEHOLDER_HOME_PAGE = `import { definePage } from "@squadbase/vantage"
9
+ import {
10
+ DashboardCardPreset,
11
+ EChart,
12
+ PageShell,
13
+ PageShellContent,
14
+ PageShellDescription,
15
+ PageShellHeader,
16
+ PageShellHeading,
17
+ PageShellTitle,
18
+ Placeholder,
19
+ Sparkline,
20
+ type EChartsOption,
21
+ } from "@squadbase/vantage/components"
22
+
23
+ export const page = definePage({
24
+ title: "Dashboard",
25
+ description: "Sample data \u2014 replace with your own to get started.",
26
+ navLabel: "Home",
27
+ })
28
+
29
+ const METRICS = [
30
+ { label: "Metric 1", value: "1,234", trend: [3, 4, 3, 5, 6, 5, 7] },
31
+ { label: "Metric 2", value: "5,678", trend: [6, 5, 6, 4, 5, 6, 6] },
32
+ { label: "Metric 3", value: "90.1%", trend: [2, 3, 5, 4, 6, 7, 8] },
33
+ { label: "Metric 4", value: "$12.3k", trend: [5, 6, 4, 6, 5, 7, 6] },
34
+ ]
35
+
36
+ // Sample charts. \`EChart\` paints series with \`--chart-1..5\` and the sparklines
37
+ // use \`--primary\` (both from \`styles.css\`), so a theme change shows up here.
38
+ const BAR_OPTION: EChartsOption = {
39
+ legend: { top: 0 },
40
+ grid: { top: 36, right: 8, bottom: 24, left: 36 },
41
+ xAxis: { type: "category", data: ["Jan", "Feb", "Mar", "Apr", "May", "Jun"] },
42
+ yAxis: { type: "value" },
43
+ series: [
44
+ { name: "Series A", type: "bar", data: [12, 18, 15, 22, 26, 24] },
45
+ { name: "Series B", type: "bar", data: [8, 11, 13, 12, 16, 19] },
46
+ { name: "Series C", type: "bar", data: [5, 7, 6, 9, 8, 11] },
47
+ ],
48
+ }
49
+
50
+ const PIE_OPTION: EChartsOption = {
51
+ legend: { bottom: 0 },
52
+ series: [
53
+ {
54
+ type: "pie",
55
+ radius: ["45%", "70%"],
56
+ center: ["50%", "45%"],
57
+ label: { show: false },
58
+ data: [
59
+ { name: "Category A", value: 35 },
60
+ { name: "Category B", value: 25 },
61
+ { name: "Category C", value: 18 },
62
+ { name: "Category D", value: 12 },
63
+ { name: "Category E", value: 10 },
64
+ ],
65
+ },
66
+ ],
67
+ }
68
+
69
+ const CHARTS = [
70
+ { title: "Chart 1", option: BAR_OPTION },
71
+ { title: "Chart 2", option: PIE_OPTION },
72
+ ]
73
+
74
+ export default function HomePage() {
75
+ return (
76
+ <PageShell>
77
+ <PageShellHeader>
78
+ <PageShellHeading>
79
+ <PageShellTitle>Dashboard</PageShellTitle>
80
+ <PageShellDescription>
81
+ Sample data \u2014 replace with your own to get started.
82
+ </PageShellDescription>
83
+ </PageShellHeading>
84
+ </PageShellHeader>
85
+
86
+ <PageShellContent>
87
+ <section className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
88
+ {METRICS.map((metric) => (
89
+ <DashboardCardPreset key={metric.label} title={metric.label}>
90
+ <Placeholder className="text-2xl font-bold">{metric.value}</Placeholder>
91
+ <Sparkline
92
+ className="mt-2"
93
+ data={metric.trend.map((value) => ({ value }))}
94
+ color="text-primary"
95
+ area
96
+ />
97
+ </DashboardCardPreset>
98
+ ))}
99
+ </section>
100
+
101
+ <section className="grid grid-cols-1 gap-4 lg:grid-cols-2">
102
+ {CHARTS.map((chart) => (
103
+ <DashboardCardPreset key={chart.title} title={chart.title}>
104
+ <EChart option={chart.option} height={256} ariaLabel={chart.title} />
105
+ </DashboardCardPreset>
106
+ ))}
107
+ </section>
108
+ </PageShellContent>
109
+ </PageShell>
110
+ )
111
+ }
112
+ `;
113
+ function isPlaceholderHomePage(content) {
114
+ if (/\bplaceholder\s*:\s*true\b/.test(content)) return true;
115
+ return content.trim() === PLACEHOLDER_HOME_PAGE.trim();
116
+ }
117
+ function isHomePagePath(file) {
118
+ return file === "index.tsx" || file === "src/index.tsx";
119
+ }
7
120
  function humanize(stem) {
8
121
  return stem.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[-_\s]+/).filter(Boolean).map((word) => word === word.toUpperCase() ? word : word[0].toUpperCase() + word.slice(1)).join(" ");
9
122
  }
@@ -80,6 +193,7 @@ function parsePageMeta(src) {
80
193
  if (title) meta.title = title;
81
194
  if (description) meta.description = description;
82
195
  if (navLabel) meta.navLabel = navLabel;
196
+ if (/\bplaceholder\s*:\s*true\b/.test(args)) meta.placeholder = true;
83
197
  return meta;
84
198
  }
85
199
  function extractPageMeta(absFile) {
@@ -98,6 +212,11 @@ function extractPageMeta(absFile) {
98
212
  return {};
99
213
  }
100
214
  const meta = parsePageMeta(src);
215
+ const fileName = absFile.split(/[/\\]/).pop() ?? "";
216
+ const relPath = absFile.includes("/src/") ? `src/${fileName}` : fileName;
217
+ if (isHomePagePath(relPath) && isPlaceholderHomePage(src)) {
218
+ meta.placeholder = true;
219
+ }
101
220
  metaCache.set(absFile, { mtimeMs: stat.mtimeMs, size: stat.size, meta });
102
221
  return meta;
103
222
  }
@@ -137,29 +256,49 @@ function buildRouteTree(model) {
137
256
  const index = remaining.length === 0;
138
257
  const path2 = index ? "/" : remaining.map(segToRouter).join("/");
139
258
  const meta = page.kind === "file" ? filePageMeta(page) : extractPageMeta(page.absFile);
140
- return {
259
+ const builtPage = {
141
260
  file: page.file,
142
261
  routePath: page.routePath,
143
262
  parentLayoutId: owner ? owner.id : null,
144
263
  path: path2,
145
264
  index,
146
- ...meta.title ? { title: meta.title } : {},
147
- ...meta.description ? { description: meta.description } : {},
148
- ...meta.navLabel ? { navLabel: meta.navLabel } : {},
149
- kind: page.kind,
150
- ...page.fileExt ? { fileExt: page.fileExt } : {},
151
- ...page.variant ? { variant: page.variant } : {}
265
+ kind: page.kind
152
266
  };
267
+ if (meta.title) builtPage.title = meta.title;
268
+ if (meta.description) builtPage.description = meta.description;
269
+ if (meta.navLabel) builtPage.navLabel = meta.navLabel;
270
+ if (page.fileExt) builtPage.fileExt = page.fileExt;
271
+ if (page.variant) builtPage.variant = page.variant;
272
+ if (meta.placeholder) builtPage.placeholder = meta.placeholder;
273
+ return builtPage;
153
274
  });
275
+ const byRoutePath = /* @__PURE__ */ new Map();
276
+ for (const page of pages) {
277
+ const group = byRoutePath.get(page.routePath) ?? { placeholder: [], nonPlaceholder: [] };
278
+ if (page.placeholder) {
279
+ group.placeholder.push(page);
280
+ } else {
281
+ group.nonPlaceholder.push(page);
282
+ }
283
+ byRoutePath.set(page.routePath, group);
284
+ }
285
+ const filteredPages = [];
286
+ for (const group of byRoutePath.values()) {
287
+ if (group.nonPlaceholder.length > 0) {
288
+ filteredPages.push(...group.nonPlaceholder);
289
+ } else {
290
+ filteredPages.push(...group.placeholder);
291
+ }
292
+ }
154
293
  return {
155
294
  rootLayout: rootLayoutRoute ? rootLayoutRoute.file : null,
156
295
  notFound: model.notFound,
157
296
  error: model.error,
158
297
  layouts: built,
159
- pages
298
+ pages: filteredPages
160
299
  };
161
300
  }
162
301
 
163
- export { buildRouteTree, extractPageMeta, filePageKind, filePageMeta, filePageModuleName };
164
- //# sourceMappingURL=chunk-KLEIVDUV.js.map
165
- //# sourceMappingURL=chunk-KLEIVDUV.js.map
302
+ export { buildRouteTree, extractPageMeta, filePageKind, filePageMeta, filePageModuleName, isHomePagePath, isPlaceholderHomePage };
303
+ //# sourceMappingURL=chunk-CEXAQQ5W.js.map
304
+ //# sourceMappingURL=chunk-CEXAQQ5W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/placeholder.ts","../src/core/file-page.ts","../src/core/route-tree.ts"],"names":["depth","path"],"mappings":"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","file":"chunk-CEXAQQ5W.js","sourcesContent":["/**\n * Detect whether a file is an untouched template placeholder.\n *\n * When a Vantage project is initialized from the template, it includes\n * a placeholder home page at `src/index.tsx` (or `index.tsx` at the root).\n * If that file remains completely untouched and a new artifact (e.g. a slide)\n * wants to claim route `/`, we allow it by treating the placeholder as yieldable\n * instead of reporting ROUTE_CONFLICT.\n */\n\n/**\n * Expected content of the template placeholder `src/index.tsx`.\n * This is the exact content from @squadbase/vantage-template 0.1.1.\n * When the template is updated, this constant must be updated as well.\n */\nconst PLACEHOLDER_HOME_PAGE = `import { definePage } from \"@squadbase/vantage\"\nimport {\n DashboardCardPreset,\n EChart,\n PageShell,\n PageShellContent,\n PageShellDescription,\n PageShellHeader,\n PageShellHeading,\n PageShellTitle,\n Placeholder,\n Sparkline,\n type EChartsOption,\n} from \"@squadbase/vantage/components\"\n\nexport const page = definePage({\n title: \"Dashboard\",\n description: \"Sample data — replace with your own to get started.\",\n navLabel: \"Home\",\n})\n\nconst METRICS = [\n { label: \"Metric 1\", value: \"1,234\", trend: [3, 4, 3, 5, 6, 5, 7] },\n { label: \"Metric 2\", value: \"5,678\", trend: [6, 5, 6, 4, 5, 6, 6] },\n { label: \"Metric 3\", value: \"90.1%\", trend: [2, 3, 5, 4, 6, 7, 8] },\n { label: \"Metric 4\", value: \"$12.3k\", trend: [5, 6, 4, 6, 5, 7, 6] },\n]\n\n// Sample charts. \\`EChart\\` paints series with \\`--chart-1..5\\` and the sparklines\n// use \\`--primary\\` (both from \\`styles.css\\`), so a theme change shows up here.\nconst BAR_OPTION: EChartsOption = {\n legend: { top: 0 },\n grid: { top: 36, right: 8, bottom: 24, left: 36 },\n xAxis: { type: \"category\", data: [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\"] },\n yAxis: { type: \"value\" },\n series: [\n { name: \"Series A\", type: \"bar\", data: [12, 18, 15, 22, 26, 24] },\n { name: \"Series B\", type: \"bar\", data: [8, 11, 13, 12, 16, 19] },\n { name: \"Series C\", type: \"bar\", data: [5, 7, 6, 9, 8, 11] },\n ],\n}\n\nconst PIE_OPTION: EChartsOption = {\n legend: { bottom: 0 },\n series: [\n {\n type: \"pie\",\n radius: [\"45%\", \"70%\"],\n center: [\"50%\", \"45%\"],\n label: { show: false },\n data: [\n { name: \"Category A\", value: 35 },\n { name: \"Category B\", value: 25 },\n { name: \"Category C\", value: 18 },\n { name: \"Category D\", value: 12 },\n { name: \"Category E\", value: 10 },\n ],\n },\n ],\n}\n\nconst CHARTS = [\n { title: \"Chart 1\", option: BAR_OPTION },\n { title: \"Chart 2\", option: PIE_OPTION },\n]\n\nexport default function HomePage() {\n return (\n <PageShell>\n <PageShellHeader>\n <PageShellHeading>\n <PageShellTitle>Dashboard</PageShellTitle>\n <PageShellDescription>\n Sample data — replace with your own to get started.\n </PageShellDescription>\n </PageShellHeading>\n </PageShellHeader>\n\n <PageShellContent>\n <section className=\"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4\">\n {METRICS.map((metric) => (\n <DashboardCardPreset key={metric.label} title={metric.label}>\n <Placeholder className=\"text-2xl font-bold\">{metric.value}</Placeholder>\n <Sparkline\n className=\"mt-2\"\n data={metric.trend.map((value) => ({ value }))}\n color=\"text-primary\"\n area\n />\n </DashboardCardPreset>\n ))}\n </section>\n\n <section className=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n {CHARTS.map((chart) => (\n <DashboardCardPreset key={chart.title} title={chart.title}>\n <EChart option={chart.option} height={256} ariaLabel={chart.title} />\n </DashboardCardPreset>\n ))}\n </section>\n </PageShellContent>\n </PageShell>\n )\n}\n`\n\n/**\n * Check if a file content matches the template placeholder home page.\n * Returns true if the file is the exact untouched placeholder.\n * \n * This is a fallback for projects created from the older template (0.1.1-dev1.2be5c44)\n * before the explicit `placeholder: true` marker was added.\n */\nexport function isPlaceholderHomePage(content: string): boolean {\n // First check for explicit placeholder marker\n if (/\\bplaceholder\\s*:\\s*true\\b/.test(content)) return true\n \n // Fallback: exact content match for older templates\n return content.trim() === PLACEHOLDER_HOME_PAGE.trim()\n}\n\n/**\n * Check if a file path is the home page (index.tsx at root or in src/).\n */\nexport function isHomePagePath(file: string): boolean {\n return file === \"index.tsx\" || file === \"src/index.tsx\"\n}\n","/**\n * Everything the rest of the framework needs to know about a data-file page:\n * its title, the token the viewer switches on, and the name of the wrapper\n * module generated for it. One source of truth, the same way\n * `route-spec.ts`'s `detectApiMethods` is the one source for API methods —\n * `route-tree.ts` (metadata) and `generate.ts` (codegen) both read from here.\n */\nimport path from \"node:path\"\nimport { createHash } from \"node:crypto\"\n\n/** The parts of a file page each helper below needs. */\ninterface FileNamed {\n /** Project-relative POSIX path. */\n file: string\n}\ninterface FileTyped {\n /** The data-file extension, e.g. `\".csv\"`. */\n fileExt?: string\n}\n/** Metadata also needs the page-root-relative directory, which the URL uses. */\ntype FileMetaInput = FileNamed & FileTyped & { dir: string }\n\n/** The same shape `definePage` metadata takes, so callers can spread either. */\ninterface PageMeta {\n title?: string\n description?: string\n navLabel?: string\n placeholder?: boolean\n}\n\n/**\n * Turn a file stem into a title: `monthly_report-2024` -> \"Monthly Report\n * 2024\", `salesByRegion` -> \"Sales By Region\". All-caps tokens are left alone\n * (`KPI` stays `KPI`) because they are almost always acronyms.\n */\nexport function humanize(stem: string): string {\n return stem\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .split(/[-_\\s]+/)\n .filter(Boolean)\n .map((word) => (word === word.toUpperCase() ? word : word[0]!.toUpperCase() + word.slice(1)))\n .join(\" \")\n}\n\n/**\n * Title for a data-file page, derived from its name — a spreadsheet cannot\n * carry a `definePage`. `index.csv` borrows its directory's name (its own name\n * is not in the URL either); a root-level `index.csv` gets no title at all,\n * exactly like a root `index.tsx` without `definePage`.\n */\nexport function filePageMeta(page: FileMetaInput): PageMeta {\n const base = path.posix.basename(page.file)\n let stem = page.fileExt ? base.slice(0, -page.fileExt.length) : base\n if (stem.toLowerCase() === \"index\") stem = page.dir.split(\"/\").filter(Boolean).pop() ?? \"\"\n if (stem === \"\") return {}\n return { title: humanize(stem) }\n}\n\n/** The token handed to the client viewer: `\".xlsx\"` -> `\"xlsx\"`. */\nexport function filePageKind(page: FileTyped): string {\n return (page.fileExt ?? \"\").slice(1)\n}\n\n/**\n * Name of the wrapper module generated for a data-file page. Derived from the\n * source path so it is stable across runs (`writeIfChanged` stays a no-op) and\n * readable in a stack trace; the hash suffix keeps `a/b.csv` and `a-b.csv` from\n * colliding once slugified.\n */\nexport function filePageModuleName(page: FileNamed): string {\n const slug = page.file\n .replace(/[^A-Za-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .toLowerCase()\n const hash = createHash(\"sha256\").update(page.file).digest(\"hex\").slice(0, 8)\n return `${slug}-${hash}.tsx`\n}\n","import fs from \"node:fs\"\nimport type { FilePageExt, PageVariant } from \"./constants\"\nimport { filePageMeta } from \"./file-page\"\nimport { isHomePagePath, isPlaceholderHomePage } from \"./placeholder\"\nimport type { PageKind, RouteModel, Segment } from \"./types\"\n\n/** A layout route (path-bearing) in the built tree. */\nexport interface BuiltLayout {\n id: string\n dir: string\n parentId: string | null\n /** Router path relative to its parent (TanStack `$param` form). */\n path: string\n file: string\n}\n\n/** A page route in the built tree. */\nexport interface BuiltPage {\n file: string\n routePath: string\n parentLayoutId: string | null\n /** Router path relative to parent, or \"/\" for an index route. */\n path: string\n index: boolean\n title?: string\n description?: string\n navLabel?: string\n kind: PageKind\n fileExt?: FilePageExt\n variant?: PageVariant\n placeholder?: boolean\n}\n\n/** The structured route tree emitted for the client runtime. */\nexport interface BuiltTree {\n rootLayout: string | null\n notFound: string | null\n error: string | null\n layouts: BuiltLayout[]\n pages: BuiltPage[]\n}\n\nfunction segToRouter(s: Segment): string {\n if (s.kind === \"static\") return s.value\n if (s.kind === \"param\") return `$${s.name}`\n return \"$\"\n}\n\n/** Convert a relative directory string (may contain `[param]`) to a router path. */\nfunction dirToRouterPath(relDir: string): string {\n return relDir\n .split(\"/\")\n .filter(Boolean)\n .map((token) => {\n const catchall = token.match(/^\\[\\.\\.\\.(.+)\\]$/)\n if (catchall) return \"$\"\n const param = token.match(/^\\[(.+)\\]$/)\n if (param) return `$${param[1]}`\n return token\n })\n .join(\"/\")\n}\n\nfunction isAncestorDir(ancestor: string, dir: string): boolean {\n if (ancestor === \"\") return true\n return dir === ancestor || dir.startsWith(ancestor + \"/\")\n}\n\nfunction isStrictAncestorDir(ancestor: string, dir: string): boolean {\n return dir.startsWith(ancestor + \"/\")\n}\n\nfunction depth(dir: string): number {\n return dir === \"\" ? 0 : dir.split(\"/\").length\n}\n\n/**\n * Extract the argument object body of the first `definePage({ ... })` call\n * using balanced-brace scanning, so nested objects/functions (e.g.\n * `validateSearch: (s) => ({ ... })`) don't truncate the match. Returns null\n * when there is no definePage call — the caller then emits no metadata rather\n * than falling back to the whole file (which would match unrelated `title:`\n * keys in chart/option configs).\n */\nfunction extractDefinePageArgs(src: string): string | null {\n const call = src.match(/\\bdefinePage\\s*\\(/)\n if (!call) return null\n let i = (call.index ?? 0) + call[0].length\n while (i < src.length && /\\s/.test(src[i]!)) i++\n if (src[i] !== \"{\") return null\n const start = i\n let depth = 0\n for (; i < src.length; i++) {\n const c = src[i]\n if (c === \"{\") depth++\n else if (c === \"}\" && --depth === 0) return src.slice(start + 1, i)\n }\n return null\n}\n\n/**\n * Match `key: \"...\"` allowing the value's own quote character to differ and\n * handling escapes, so values containing an apostrophe/quote are not truncated.\n */\nfunction matchStringField(scope: string, key: string): string | undefined {\n const re = new RegExp(\n `\\\\b${key}\\\\s*:\\\\s*(?:\"((?:\\\\\\\\.|[^\"\\\\\\\\])*)\"|'((?:\\\\\\\\.|[^'\\\\\\\\])*)'|\\`((?:\\\\\\\\.|[^\\`\\\\\\\\])*)\\`)`,\n )\n const m = scope.match(re)\n if (!m) return undefined\n return (m[1] ?? m[2] ?? m[3] ?? \"\").replace(/\\\\(.)/g, \"$1\")\n}\n\ninterface PageMeta {\n title?: string\n description?: string\n navLabel?: string\n placeholder?: boolean\n}\n\n/**\n * Cache keyed on mtime+size. A long-lived dev server regenerates the route tree\n * on every structural change, and re-reading + re-parsing every page module for\n * metadata that almost never changes is pure waste. `statSync` is an order of\n * magnitude cheaper than the read plus the brace scan it feeds.\n */\nconst metaCache = new Map<string, { mtimeMs: number; size: number; meta: PageMeta }>()\n\nfunction parsePageMeta(src: string): PageMeta {\n const args = extractDefinePageArgs(src)\n if (args == null) return {}\n const meta: PageMeta = {}\n const title = matchStringField(args, \"title\")\n const description = matchStringField(args, \"description\")\n const navLabel = matchStringField(args, \"navLabel\")\n if (title) meta.title = title\n if (description) meta.description = description\n if (navLabel) meta.navLabel = navLabel\n // Check for placeholder: true (boolean literal)\n if (/\\bplaceholder\\s*:\\s*true\\b/.test(args)) meta.placeholder = true\n return meta\n}\n\n/** Best-effort static extraction of definePage title/description/navLabel. */\nexport function extractPageMeta(absFile: string): PageMeta {\n let stat: fs.Stats\n try {\n stat = fs.statSync(absFile)\n } catch {\n return {}\n }\n const hit = metaCache.get(absFile)\n if (hit && hit.mtimeMs === stat.mtimeMs && hit.size === stat.size) return hit.meta\n\n let src: string\n try {\n src = fs.readFileSync(absFile, \"utf8\")\n } catch {\n return {}\n }\n const meta = parsePageMeta(src)\n \n // Check for placeholder using the robust detection from placeholder.ts\n // This includes both explicit marker and fallback exact match\n const fileName = absFile.split(/[/\\\\]/).pop() ?? \"\"\n const relPath = absFile.includes(\"/src/\") ? `src/${fileName}` : fileName\n if (isHomePagePath(relPath) && isPlaceholderHomePage(src)) {\n meta.placeholder = true\n }\n \n metaCache.set(absFile, { mtimeMs: stat.mtimeMs, size: stat.size, meta })\n return meta\n}\n\n/**\n * Build the structured route tree from a scan. Layouts become path-bearing\n * routes; pages attach to the deepest layout that owns their directory.\n */\nexport function buildRouteTree(model: RouteModel): BuiltTree {\n const rootLayoutRoute = model.layouts.find((l) => l.dir === \"\") ?? null\n const nestedLayouts = model.layouts\n .filter((l) => l.dir !== \"\")\n .sort((a, b) => depth(a.dir) - depth(b.dir))\n\n const built: BuiltLayout[] = []\n const dirToId = new Map<string, string>()\n\n nestedLayouts.forEach((layout, i) => {\n const id = `L${i}`\n // Parent = deepest other layout that is a strict ancestor of this dir.\n let parent: { id: string; dir: string } | null = null\n for (const cand of built) {\n if (isStrictAncestorDir(cand.dir, layout.dir)) {\n if (!parent || depth(cand.dir) > depth(parent.dir)) parent = { id: cand.id, dir: cand.dir }\n }\n }\n const parentDir = parent ? parent.dir : \"\"\n const relDir = parentDir === \"\" ? layout.dir : layout.dir.slice(parentDir.length + 1)\n built.push({\n id,\n dir: layout.dir,\n parentId: parent ? parent.id : null,\n path: dirToRouterPath(relDir),\n file: layout.file,\n })\n dirToId.set(layout.dir, id)\n })\n\n const pages: BuiltPage[] = model.pages.map((page) => {\n // Owner = deepest layout whose dir is an ancestor-or-equal of the page dir.\n let owner: BuiltLayout | null = null\n for (const l of built) {\n if (isAncestorDir(l.dir, page.dir)) {\n if (!owner || depth(l.dir) > depth(owner.dir)) owner = l\n }\n }\n const ownerSegCount = owner ? depth(owner.dir) : 0\n const remaining = page.segments.slice(ownerSegCount)\n const index = remaining.length === 0\n const path = index ? \"/\" : remaining.map(segToRouter).join(\"/\")\n // `extractPageMeta` reads the file as UTF-8 and runs regexes over it, so a\n // data file must never reach it — a few MB of spreadsheet binary would be\n // decoded and scanned for a `definePage` that cannot exist there.\n const meta = page.kind === \"file\" ? filePageMeta(page) : extractPageMeta(page.absFile)\n const builtPage: BuiltPage = {\n file: page.file,\n routePath: page.routePath,\n parentLayoutId: owner ? owner.id : null,\n path,\n index,\n kind: page.kind,\n }\n if (meta.title) builtPage.title = meta.title\n if (meta.description) builtPage.description = meta.description\n if (meta.navLabel) builtPage.navLabel = meta.navLabel\n if (page.fileExt) builtPage.fileExt = page.fileExt\n if (page.variant) builtPage.variant = page.variant\n if (meta.placeholder) builtPage.placeholder = meta.placeholder\n return builtPage\n })\n\n // Filter out placeholder pages when there's another page at the same route.\n // This allows new artifacts (e.g. index.slide.tsx) to claim \"/\" when the\n // template's placeholder home page is untouched.\n const byRoutePath = new Map<string, { placeholder: BuiltPage[]; nonPlaceholder: BuiltPage[] }>()\n for (const page of pages) {\n const group = byRoutePath.get(page.routePath) ?? { placeholder: [], nonPlaceholder: [] }\n if (page.placeholder) {\n group.placeholder.push(page)\n } else {\n group.nonPlaceholder.push(page)\n }\n byRoutePath.set(page.routePath, group)\n }\n\n const filteredPages: BuiltPage[] = []\n for (const group of byRoutePath.values()) {\n // If there are non-placeholder pages, use only those\n if (group.nonPlaceholder.length > 0) {\n filteredPages.push(...group.nonPlaceholder)\n } else {\n // Otherwise, use the placeholder(s)\n filteredPages.push(...group.placeholder)\n }\n }\n\n return {\n rootLayout: rootLayoutRoute ? rootLayoutRoute.file : null,\n notFound: model.notFound,\n error: model.error,\n layouts: built,\n pages: filteredPages,\n }\n}\n"]}
@@ -20,9 +20,11 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
20
20
  "no-strict-port",
21
21
  "overlay",
22
22
  "no-overlay",
23
+ "draft-preview",
23
24
  "h",
24
25
  "v"
25
26
  ]);
27
+ var OPTIONAL_VALUE_FLAGS = /* @__PURE__ */ new Set(["host"]);
26
28
  function parseArgs(argv) {
27
29
  const positionals = [];
28
30
  const flags = {};
@@ -40,6 +42,15 @@ function parseArgs(argv) {
40
42
  continue;
41
43
  }
42
44
  const next = argv[i + 1];
45
+ if (OPTIONAL_VALUE_FLAGS.has(key)) {
46
+ if (next !== void 0 && !next.startsWith("-")) {
47
+ flags[key] = next;
48
+ i++;
49
+ } else {
50
+ flags[key] = true;
51
+ }
52
+ continue;
53
+ }
43
54
  if (next !== void 0 && !next.startsWith("-")) {
44
55
  flags[key] = next;
45
56
  i++;
@@ -74,10 +85,42 @@ function resolvePortOptions(flags, fallback) {
74
85
  const strictPort = flags["no-strict-port"] ? false : Boolean(flags["strict-port"]) || raw !== void 0;
75
86
  return { ok: true, port, strictPort };
76
87
  }
88
+ var ALLOWED_HOSTS_ENV = "VANTAGE_ALLOWED_HOSTS";
89
+ function resolveHostOptions(flags, env = process.env) {
90
+ const options = {};
91
+ const host = flags.host;
92
+ if (host === true || host === "") options.host = true;
93
+ else if (typeof host === "string") options.host = host;
94
+ const flag = flags["allowed-hosts"];
95
+ if (flag === true) {
96
+ return {
97
+ ok: false,
98
+ error: `--allowed-hosts needs a comma-separated list of hostnames, or "true" to allow any`
99
+ };
100
+ }
101
+ const raw = typeof flag === "string" ? flag : env[ALLOWED_HOSTS_ENV];
102
+ const allowedHosts = parseAllowedHosts(raw);
103
+ if (allowedHosts !== void 0) options.allowedHosts = allowedHosts;
104
+ return { ok: true, options };
105
+ }
106
+ function parseAllowedHosts(raw) {
107
+ if (raw === void 0) return void 0;
108
+ const trimmed = raw.trim();
109
+ if (trimmed === "") return void 0;
110
+ if (trimmed === "true") return true;
111
+ const hosts = trimmed.split(",").map((h) => h.trim()).filter((h) => h.length > 0);
112
+ return hosts.length ? hosts : void 0;
113
+ }
77
114
  function resolveHmrOverlay(flags) {
78
115
  if (flags.overlay) return true;
79
116
  return !flags["no-overlay"];
80
117
  }
118
+ function resolveDraftPreview(flags) {
119
+ const flag = flags["draft-preview"];
120
+ if (flag === true || flag === "true" || flag === "1") return true;
121
+ if (flag !== void 0) return false;
122
+ return process.env.VANTAGE_DRAFT_PREVIEW === "1";
123
+ }
81
124
  function reportPortInUse(err, port, strictPort) {
82
125
  if (!strictPort || !(err instanceof Error) || !/already in use/.test(err.message)) return false;
83
126
  log.error(`Port ${port} is already in use.`);
@@ -137,6 +180,6 @@ function frameworkVersion() {
137
180
  }
138
181
  }
139
182
 
140
- export { applyApiBaseUrlFlag, frameworkVersion, log, parseArgs, printDiagnostics, printJson, reportPortInUse, resolveHmrOverlay, resolvePortOptions, resolveQuickRoot, resolveRoot };
141
- //# sourceMappingURL=chunk-3G43C6OL.js.map
142
- //# sourceMappingURL=chunk-3G43C6OL.js.map
183
+ export { applyApiBaseUrlFlag, frameworkVersion, log, parseArgs, printDiagnostics, printJson, reportPortInUse, resolveDraftPreview, resolveHmrOverlay, resolveHostOptions, resolvePortOptions, resolveQuickRoot, resolveRoot };
184
+ //# sourceMappingURL=chunk-GNXCX4WE.js.map
185
+ //# sourceMappingURL=chunk-GNXCX4WE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/util.ts"],"names":[],"mappings":"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","file":"chunk-GNXCX4WE.js","sourcesContent":["import fs from \"node:fs\"\nimport path from \"node:path\"\nimport pc from \"picocolors\"\nimport { PAGE_EXTENSIONS } from \"../core/constants\"\nimport type { Diagnostic } from \"../core/types\"\n\n/** Parsed CLI arguments. */\nexport interface ParsedArgs {\n positionals: string[]\n flags: Record<string, string | boolean>\n}\n\n/** Flags that never take a value, so they don't swallow the next token. */\nconst BOOLEAN_FLAGS = new Set([\n \"json\",\n \"help\",\n \"force\",\n \"verbose\",\n \"version\",\n \"all\",\n \"list\",\n \"regex\",\n \"pages\",\n \"apis\",\n \"detail\",\n \"strict-port\",\n \"no-strict-port\",\n \"overlay\",\n \"no-overlay\",\n \"draft-preview\",\n \"h\",\n \"v\",\n])\n\n/**\n * Flags whose value is optional: bare `--host` means `true` (all interfaces),\n * `--host 0.0.0.0` / `--host=::` carry the address. The next token is taken as\n * the value only when it does not look like another flag, so `--host --port 3000`\n * still reads as two flags. Write `--host=<addr>` when the following token could\n * be mistaken for the value (e.g. a positional).\n */\nconst OPTIONAL_VALUE_FLAGS = new Set([\"host\"])\n\n/** Minimal argv parser: `--json`, `--port 3000`, `--port=3000`, `-v`, positionals. */\nexport function parseArgs(argv: string[]): ParsedArgs {\n const positionals: string[] = []\n const flags: Record<string, string | boolean> = {}\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i]!\n if (arg.startsWith(\"--\")) {\n const eq = arg.indexOf(\"=\")\n if (eq !== -1) {\n flags[arg.slice(2, eq)] = arg.slice(eq + 1)\n continue\n }\n const key = arg.slice(2)\n if (BOOLEAN_FLAGS.has(key)) {\n flags[key] = true\n continue\n }\n const next = argv[i + 1]\n if (OPTIONAL_VALUE_FLAGS.has(key)) {\n if (next !== undefined && !next.startsWith(\"-\")) {\n flags[key] = next\n i++\n } else {\n flags[key] = true\n }\n continue\n }\n if (next !== undefined && !next.startsWith(\"-\")) {\n flags[key] = next\n i++\n } else {\n flags[key] = true\n }\n } else if (arg.length > 1 && arg.startsWith(\"-\") && !/^-\\d/.test(arg)) {\n // Single-dash short flags, e.g. `-v`, `-h` (each char is a boolean).\n for (const ch of arg.slice(1)) flags[ch] = true\n } else {\n positionals.push(arg)\n }\n }\n return { positionals, flags }\n}\n\n/**\n * Apply a `--api-base-url <url>` flag by setting `process.env.PUBLIC_API_BASE_URL`\n * so Vite inlines it into the client bundle (via envPrefix). Setting it on\n * process.env gives the flag precedence over any `.env` file value. No-op when\n * the flag is absent, leaving any `.env` value in effect.\n */\nexport function applyApiBaseUrlFlag(args: ParsedArgs): void {\n const value = args.flags[\"api-base-url\"]\n if (typeof value === \"string\" && value.length) {\n process.env.PUBLIC_API_BASE_URL = value\n }\n}\n\n/** A resolved `--port`, or the reason it could not be resolved. */\nexport type PortResolution =\n | { ok: true; port: number; strictPort: boolean }\n | { ok: false; error: string }\n\n/**\n * Resolve `--port` / `--strict-port` / `--no-strict-port` into Vite's server\n * options.\n *\n * An explicit `--port` is strict by default: writing `--port 3000` means the\n * port is a contract with something outside this process — a reverse proxy, a\n * container port mapping, an OAuth redirect URI — so binding 3001 instead is a\n * silent failure, not a recovery (a supervisor that only checks \"is anything\n * listening on 3000\" will happily report a server that nobody can reach).\n * Without the flag the default is merely \"some free port\", so Vite keeps\n * walking to the next one as it always has, and running two projects at once\n * needs no ceremony. `--strict-port` / `--no-strict-port` override either way.\n *\n * A bad value is an error rather than a silent fallback, for the same reason:\n * a typo'd `--port` that quietly became 5173 is exactly the failure strictness\n * exists to prevent.\n */\nexport function resolvePortOptions(\n flags: Record<string, string | boolean>,\n fallback: number,\n): PortResolution {\n const raw = flags.port\n let port = fallback\n if (raw !== undefined) {\n if (typeof raw !== \"string\") return { ok: false, error: \"--port needs a number, e.g. --port 3000\" }\n const n = Number(raw)\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n return { ok: false, error: `Invalid --port \"${raw}\" — use an integer between 1 and 65535.` }\n }\n port = n\n }\n const strictPort = flags[\"no-strict-port\"]\n ? false\n : Boolean(flags[\"strict-port\"]) || raw !== undefined\n return { ok: true, port, strictPort }\n}\n\n/** Environment variable read for `allowedHosts` when the flag is absent. */\nexport const ALLOWED_HOSTS_ENV = \"VANTAGE_ALLOWED_HOSTS\"\n\n/** Resolved `--host` / `--allowed-hosts`, or the reason they could not be resolved. */\nexport type HostResolution =\n | { ok: true; options: { host?: string | true; allowedHosts?: string[] | true } }\n | { ok: false; error: string }\n\n/**\n * Resolve `--host [addr]` and `--allowed-hosts <list|true>` (falling back to\n * `VANTAGE_ALLOWED_HOSTS`) into the subset of Vite's server options they map to.\n *\n * Only the keys that were actually set are returned, so spreading the result\n * into `server` / `preview` leaves Vite's defaults untouched when neither is\n * given — the default behaviour must not change for anyone who never asked.\n *\n * `allowedHosts` exists because Vite rejects (403) any request whose `Host`\n * is not localhost or the bound address. A reverse proxy in front of the dev\n * server — a sandbox preview proxy, a tunnel, a container network — presents\n * its own hostname, so that hostname must be listed (or the check disabled\n * with the literal `true`). The env form is for supervisors that start the\n * server and know the hostname; an explicit flag still wins over it.\n */\nexport function resolveHostOptions(\n flags: Record<string, string | boolean>,\n env: NodeJS.ProcessEnv = process.env,\n): HostResolution {\n const options: { host?: string | true; allowedHosts?: string[] | true } = {}\n\n const host = flags.host\n // bare `--host` and `--host=` both mean \"every interface\"\n if (host === true || host === \"\") options.host = true\n else if (typeof host === \"string\") options.host = host\n\n const flag = flags[\"allowed-hosts\"]\n if (flag === true) {\n return {\n ok: false,\n error: `--allowed-hosts needs a comma-separated list of hostnames, or \"true\" to allow any`,\n }\n }\n const raw = typeof flag === \"string\" ? flag : env[ALLOWED_HOSTS_ENV]\n const allowedHosts = parseAllowedHosts(raw)\n if (allowedHosts !== undefined) options.allowedHosts = allowedHosts\n\n return { ok: true, options }\n}\n\n/** `\"a,b\"` → `[\"a\",\"b\"]`, the literal `true` → `true`, unset/blank → undefined (Vite default). */\nfunction parseAllowedHosts(raw: string | undefined): string[] | true | undefined {\n if (raw === undefined) return undefined\n const trimmed = raw.trim()\n if (trimmed === \"\") return undefined\n if (trimmed === \"true\") return true\n const hosts = trimmed\n .split(\",\")\n .map((h) => h.trim())\n .filter((h) => h.length > 0)\n return hosts.length ? hosts : undefined\n}\n\n/**\n * Resolve `--overlay` / `--no-overlay` into Vite's `server.hmr.overlay`.\n *\n * The overlay stays on by default — it is the fastest way for a human to see a\n * runtime error. `--no-overlay` is for the cases where a full-screen error card\n * is in the way rather than helpful: screenshots and demos, and agent-driven\n * sessions where the error already arrives in the terminal through console\n * forwarding and the overlay only obscures the page being inspected.\n */\nexport function resolveHmrOverlay(flags: Record<string, string | boolean>): boolean {\n if (flags.overlay) return true\n return !flags[\"no-overlay\"]\n}\n\n/**\n * Whether `dev` accepts unfinished files (`--draft-preview` or\n * `VANTAGE_DRAFT_PREVIEW=1`). Off by default: the endpoint it opens is only for\n * a supervisor (the Squadbase sandbox) that streams an agent's half-written\n * files into the preview, and a plain dev server should not have it.\n */\nexport function resolveDraftPreview(flags: Record<string, string | boolean>): boolean {\n const flag = flags[\"draft-preview\"]\n if (flag === true || flag === \"true\" || flag === \"1\") return true\n if (flag !== undefined) return false\n return process.env.VANTAGE_DRAFT_PREVIEW === \"1\"\n}\n\n/**\n * Report a strict bind that lost the port, in the one shape dev and preview\n * share. Returns false when `err` is anything else, so the caller rethrows.\n * Vite throws a plain Error (no `code`) for this, hence the message match — a\n * reworded Vite message degrades to the generic handler, not to a wrong one.\n */\nexport function reportPortInUse(err: unknown, port: number, strictPort: boolean): boolean {\n if (!strictPort || !(err instanceof Error) || !/already in use/.test(err.message)) return false\n log.error(`Port ${port} is already in use.`)\n log.info(\" free that port, pass a different --port, or --no-strict-port to take the next one\")\n return true\n}\n\n/** Resolve the project root, honoring `--root`, defaulting to cwd. */\nexport function resolveRoot(flags: Record<string, string | boolean>): string {\n const raw = typeof flags.root === \"string\" ? flags.root : process.cwd()\n return path.resolve(raw)\n}\n\n/** How far up from a page file to look for the package root. */\nconst QUICK_ROOT_MAX_DEPTH = 5\n\n/**\n * Resolve a positional path to a quick-run project root.\n * A directory → itself; a page file (.tsx/.jsx) → its project; else null.\n */\nexport function resolveQuickRoot(target: string): string | null {\n try {\n const abs = path.resolve(target)\n const stat = fs.statSync(abs)\n if (stat.isDirectory()) return abs\n if (stat.isFile() && PAGE_EXTENSIONS.some((ext) => abs.endsWith(ext))) {\n // Walk up to the nearest package.json so `vantage ./demo/src/index.tsx`\n // starts in ./demo — the directory holding the page is not the project\n // root once pages live under src/ (or in any nested route directory).\n let dir = path.dirname(abs)\n for (let i = 0; i <= QUICK_ROOT_MAX_DEPTH; i++) {\n if (fs.existsSync(path.join(dir, \"package.json\"))) return dir\n const parent = path.dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n return path.dirname(abs)\n }\n } catch {\n // statSync throws when the path does not exist → null (not a quick target)\n }\n return null\n}\n\nexport const log = {\n info: (msg: string) => console.log(msg),\n step: (msg: string) => console.log(pc.cyan(\"→ \") + msg),\n ok: (msg: string) => console.log(pc.green(\"✓ \") + msg),\n warn: (msg: string) => console.log(pc.yellow(\"! \") + msg),\n error: (msg: string) => console.error(pc.red(\"✗ \") + msg),\n brand: (msg: string) => console.log(pc.bold(pc.magenta(\"vantage \")) + msg),\n}\n\n/** Print diagnostics as human-readable text. */\nexport function printDiagnostics(diags: Diagnostic[]): void {\n for (const d of diags) {\n const badge =\n d.severity === \"error\" ? pc.red(pc.bold(\" ERROR \")) : pc.yellow(pc.bold(\" WARN \"))\n console.log(`${badge} ${pc.dim(d.code)} ${d.message}`)\n if (d.files?.length) console.log(` ${pc.dim(\"files:\")} ${d.files.join(\", \")}`)\n if (d.fix) console.log(` ${pc.dim(\"fix:\")} ${d.fix}`)\n }\n}\n\n/** Emit a JSON payload to stdout (for agent consumption). */\nexport function printJson(payload: unknown): void {\n process.stdout.write(JSON.stringify(payload, null, 2) + \"\\n\")\n}\n\n/** Read the vantage package version. */\nexport function frameworkVersion(): string {\n try {\n // dist/cli.js (and shared chunks) live in dist/, so package.json is one up.\n const url = new URL(\"../package.json\", import.meta.url)\n const pkg = JSON.parse(fs.readFileSync(url, \"utf8\")) as { version: string }\n return pkg.version\n } catch {\n return \"0.0.0\"\n }\n}\n\nexport function fileExists(p: string): boolean {\n try {\n return fs.existsSync(p)\n } catch {\n return false\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { filePageMeta, extractPageMeta } from './chunk-KLEIVDUV.js';
1
+ import { filePageMeta, extractPageMeta } from './chunk-CEXAQQ5W.js';
2
2
  import { API_METHODS } from './chunk-C2CX6GLX.js';
3
3
  import fs from 'fs';
4
4
 
@@ -237,5 +237,5 @@ function extractApiSpec(api) {
237
237
  }
238
238
 
239
239
  export { detectApiMethods, extractApiSpec, extractPageSpec, maskSource, stripComments };
240
- //# sourceMappingURL=chunk-JGRMRXXZ.js.map
241
- //# sourceMappingURL=chunk-JGRMRXXZ.js.map
240
+ //# sourceMappingURL=chunk-JKLLFVEU.js.map
241
+ //# sourceMappingURL=chunk-JKLLFVEU.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/source.ts","../src/core/route-spec.ts"],"names":[],"mappings":"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","file":"chunk-JGRMRXXZ.js","sourcesContent":["/**\n * Low-level source scanning helpers shared by the static analyzers\n * (`diagnostics.ts`, `route-spec.ts`). Everything here is text-only — user code\n * is never imported or executed.\n */\n\n/**\n * Strip block and line comments so commented-out code doesn't produce false\n * positives (or hide real issues). Naive but adequate for presence checks:\n * URLs (`http://`) are preserved via the `[^:]` guard before `//`.\n */\nexport function stripComments(src: string): string {\n return src.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\").replace(/(^|[^:\"'`\\\\])\\/\\/[^\\n]*/g, \"$1\")\n}\n\nconst OPENERS: Record<string, string> = { \"(\": \")\", \"[\": \"]\", \"{\": \"}\" }\n\n/**\n * Read a balanced bracketed region starting at `openIndex` (which must hold\n * `(`, `[` or `{`), returning the raw text *between* the brackets. Quote- and\n * nesting-aware so brackets inside string literals don't unbalance the scan.\n * Returns null when the region is never closed.\n */\nexport function readBracketBody(src: string, openIndex: number): string | null {\n const open = src[openIndex]\n if (!open || !OPENERS[open]) return null\n let depth = 0\n let quote: string | null = null\n for (let i = openIndex; i < src.length; i++) {\n const c = src[i]!\n if (quote) {\n if (c === \"\\\\\") i++\n else if (c === quote) quote = null\n continue\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n quote = c\n continue\n }\n if (OPENERS[c]) depth++\n else if (c === \")\" || c === \"]\" || c === \"}\") {\n if (--depth === 0) return src.slice(openIndex + 1, i)\n }\n }\n return null\n}\n\n/**\n * Split an argument list or object body on top-level commas, ignoring commas\n * nested inside brackets or string literals. Empty parts are dropped.\n */\nexport function splitTopLevel(body: string): string[] {\n const parts: string[] = []\n let depth = 0\n let quote: string | null = null\n let start = 0\n for (let i = 0; i < body.length; i++) {\n const c = body[i]!\n if (quote) {\n if (c === \"\\\\\") i++\n else if (c === quote) quote = null\n continue\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n quote = c\n continue\n }\n if (OPENERS[c]) depth++\n else if (c === \")\" || c === \"]\" || c === \"}\") depth--\n else if (c === \",\" && depth === 0) {\n parts.push(body.slice(start, i))\n start = i + 1\n }\n }\n parts.push(body.slice(start))\n return parts.map((p) => p.trim()).filter(Boolean)\n}\n\n/**\n * Lex a module just far enough to tell code, comments and string literals\n * apart, *without* moving anything: comments come back blanked to spaces\n * (newlines kept) so offsets and line numbers still match the raw file, and\n * `inString[i]` is 1 where offset `i` sits inside a `'…'`, `\"…\"` or `` `…` ``\n * literal (template `${…}` bodies count as string — close enough for literal\n * scans). `'`/`\"` literals end at a newline, so an apostrophe in JSX text\n * cannot swallow more than the rest of its own line.\n */\nexport function maskSource(src: string): { code: string; inString: Uint8Array } {\n const out = src.split(\"\")\n const inString = new Uint8Array(src.length)\n let i = 0\n while (i < src.length) {\n const c = src[i]!\n const next = src[i + 1]\n if (c === \"/\" && next === \"/\" && src[i - 1] !== \":\") {\n while (i < src.length && src[i] !== \"\\n\") out[i++] = \" \"\n continue\n }\n if (c === \"/\" && next === \"*\") {\n const end = src.indexOf(\"*/\", i + 2)\n const stop = end === -1 ? src.length : end + 2\n for (; i < stop; i++) if (src[i] !== \"\\n\") out[i] = \" \"\n continue\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n i++\n while (i < src.length) {\n const d = src[i]!\n if (d === \"\\\\\") {\n inString[i] = 1\n inString[i + 1] = 1\n i += 2\n continue\n }\n if (d === c) break\n if (d === \"\\n\" && c !== \"`\") break\n inString[i++] = 1\n }\n i++\n continue\n }\n i++\n }\n return { code: out.join(\"\"), inString }\n}\n","import fs from \"node:fs\"\nimport { API_METHODS, type ApiMethod } from \"./constants\"\nimport { filePageMeta } from \"./file-page\"\nimport { extractPageMeta } from \"./route-tree\"\nimport { readBracketBody, splitTopLevel, stripComments } from \"./source\"\nimport type { ApiRoute, PageRoute, Segment } from \"./types\"\n\n/**\n * Best-effort *static* specs for the routes `vantage routes --detail` prints:\n * what a page takes in its path, and what an API route accepts and answers\n * with. Like `diagnostics.ts` this only reads text — user modules are never\n * imported or executed — so every field is a hint derived from the source, not\n * a guarantee. Callers pass sources through {@link stripComments} first.\n */\n\n/** A dynamic segment of a route path. */\nexport interface RouteParam {\n name: string\n kind: \"param\" | \"catchall\"\n}\n\n/** Static spec of a page route. */\nexport interface PageSpec {\n params: RouteParam[]\n title?: string\n description?: string\n navLabel?: string\n}\n\n/** The request body an API handler reads, when detectable. */\nexport interface ApiBodySpec {\n format: \"json\" | \"formData\" | \"text\" | \"binary\"\n /** Top-level keys, when the handler destructures them. */\n keys?: string[]\n}\n\n/** A response an API handler returns. */\nexport interface ApiResponseSpec {\n status: number\n /** Response content type, omitted when it can't be derived. */\n contentType?: string\n /** Top-level keys, when the payload is an object literal. */\n keys?: string[]\n}\n\n/** An `HttpError` an API handler throws. */\nexport interface ApiErrorSpec {\n status: number\n message?: string\n}\n\n/** Static spec of one HTTP method handler. */\nexport interface ApiMethodSpec {\n method: ApiMethod\n /** Query string keys read via `searchParams`. */\n query: string[]\n body?: ApiBodySpec\n responses: ApiResponseSpec[]\n errors: ApiErrorSpec[]\n}\n\n/** Static spec of an API route module. */\nexport interface ApiSpec {\n params: RouteParam[]\n methods: ApiMethodSpec[]\n}\n\n/**\n * HTTP method handlers a module exports (direct declaration or export list).\n * `src` must be comment-stripped. Shared with the `INVALID_API_EXPORT`\n * diagnostic so both agree on what counts as a handler.\n */\nexport function detectApiMethods(src: string): ApiMethod[] {\n return API_METHODS.filter(\n (m) =>\n new RegExp(`export\\\\s+(async\\\\s+)?(function|const|let|var)\\\\s+${m}\\\\b`).test(src) ||\n new RegExp(`export\\\\s*\\\\{[^}]*\\\\b${m}\\\\b[^}]*\\\\}`).test(src),\n )\n}\n\n/**\n * Slice the module into one source region per handler: from a handler's export\n * declaration to the next one (or EOF). Coarse on purpose — matching a function\n * body would have to cope with arrow bodies, overloads and trailing helpers —\n * and good enough because handlers are written one after another. A method\n * exported only through an export list gets the whole module as its region.\n */\nfunction methodRegions(src: string): Map<ApiMethod, string> {\n const re = new RegExp(\n `export\\\\s+(?:async\\\\s+)?(?:function|const|let|var)\\\\s+(${API_METHODS.join(\"|\")})\\\\b`,\n \"g\",\n )\n const decls: { method: ApiMethod; index: number }[] = []\n let m: RegExpExecArray | null\n while ((m = re.exec(src))) decls.push({ method: m[1] as ApiMethod, index: m.index })\n\n const regions = new Map<ApiMethod, string>()\n decls.forEach((d, i) => {\n regions.set(d.method, src.slice(d.index, decls[i + 1]?.index ?? src.length))\n })\n for (const method of detectApiMethods(src)) {\n if (!regions.has(method)) regions.set(method, src)\n }\n return regions\n}\n\n/** Top-level keys of an object literal expression, `…` standing in for spreads. */\nfunction objectKeys(expr: string): string[] | undefined {\n const trimmed = expr.trim()\n if (!trimmed.startsWith(\"{\")) return undefined\n const body = readBracketBody(trimmed, 0)\n if (body == null) return undefined\n const keys: string[] = []\n for (const part of splitTopLevel(body)) {\n if (part.startsWith(\"...\")) {\n keys.push(\"…\")\n continue\n }\n const name = part.match(/^[\"'`]?([A-Za-z0-9_$]+)/)\n if (name) keys.push(name[1]!)\n }\n return keys.length ? keys : undefined\n}\n\n/** Read `status: 201` out of a `ResponseInit` literal. */\nfunction initStatus(init: string | undefined): number | undefined {\n const m = init?.match(/\\bstatus\\s*:\\s*(\\d{3})\\b/)\n return m ? Number(m[1]) : undefined\n}\n\n/** Read a content-type header out of a `ResponseInit` literal. */\nfunction initContentType(init: string | undefined): string | undefined {\n const m = init?.match(/[\"']?content-type[\"']?\\s*:\\s*[\"']([^\"';]+)/i)\n return m ? m[1]!.trim() : undefined\n}\n\n/** Every `Response.json(…)` / `json(…)` / `new Response(…)` in a handler region. */\nfunction extractResponses(region: string): ApiResponseSpec[] {\n const re = /\\bnew\\s+Response\\s*\\(|\\bResponse\\s*\\.\\s*json\\s*\\(|(?<![.\\w$])json\\s*\\(/g\n const out: ApiResponseSpec[] = []\n const seen = new Set<string>()\n let m: RegExpExecArray | null\n while ((m = re.exec(region))) {\n const isJson = !m[0].startsWith(\"new\")\n const args = readBracketBody(region, m.index + m[0].length - 1)\n if (args == null) continue\n const [payload, init] = splitTopLevel(args)\n const contentType = isJson\n ? \"application/json\"\n : (initContentType(init) ??\n (payload?.startsWith(\"JSON.stringify\") ? \"application/json\" : undefined))\n const keys = isJson && payload ? objectKeys(payload) : undefined\n const spec: ApiResponseSpec = {\n status: initStatus(init) ?? 200,\n ...(contentType ? { contentType } : {}),\n ...(keys ? { keys } : {}),\n }\n const key = JSON.stringify(spec)\n if (seen.has(key)) continue\n seen.add(key)\n out.push(spec)\n }\n return out\n}\n\n/** Every `new HttpError(status, \"message\")` in a handler region. */\nfunction extractErrors(region: string): ApiErrorSpec[] {\n const re = /\\bHttpError\\s*\\(\\s*(\\d{3})\\s*(?:,\\s*(?:([\"'`])((?:\\\\.|(?!\\2)[\\s\\S])*)\\2))?/g\n const out: ApiErrorSpec[] = []\n const seen = new Set<string>()\n let m: RegExpExecArray | null\n while ((m = re.exec(region))) {\n const message = m[3]?.replace(/\\\\(.)/g, \"$1\")\n const spec: ApiErrorSpec = { status: Number(m[1]), ...(message ? { message } : {}) }\n const key = JSON.stringify(spec)\n if (seen.has(key)) continue\n seen.add(key)\n out.push(spec)\n }\n return out\n}\n\n/** Query keys read through `searchParams.get/getAll/has(\"x\")`. */\nfunction extractQuery(region: string): string[] {\n const re = /searchParams\\s*\\.\\s*(?:get|getAll|has)\\s*\\(\\s*[\"'`]([^\"'`]+)[\"'`]/g\n const out = new Set<string>()\n let m: RegExpExecArray | null\n while ((m = re.exec(region))) out.add(m[1]!)\n return [...out]\n}\n\n/** The request body a handler reads, plus destructured keys when present. */\nfunction extractBody(region: string): ApiBodySpec | undefined {\n const read = region.match(/\\b(?:request|req)\\s*\\.\\s*(json|formData|text|arrayBuffer|blob)\\s*\\(/)\n if (!read) return undefined\n const method = read[1]!\n const format: ApiBodySpec[\"format\"] =\n method === \"json\" || method === \"formData\" || method === \"text\" ? method : \"binary\"\n\n const destructured = region.match(\n /(?:const|let|var)\\s*(\\{[\\s\\S]*?\\})\\s*=\\s*(?:await\\s+)?[\\w.]*\\.json\\s*\\(/,\n )\n const keys = format === \"json\" && destructured ? objectKeys(destructured[1]!) : undefined\n return { format, ...(keys ? { keys } : {}) }\n}\n\nfunction toParams(segments: Segment[]): RouteParam[] {\n const out: RouteParam[] = []\n for (const s of segments) {\n if (s.kind === \"static\") continue\n out.push({ name: s.name, kind: s.kind })\n }\n return out\n}\n\n/** Static spec of a page: its path params and its `definePage` metadata. */\nexport function extractPageSpec(page: PageRoute): PageSpec {\n // A data file has no `definePage` to read, and reading it as UTF-8 would\n // decode megabytes of spreadsheet binary; its title is derived from the name.\n const meta = page.kind === \"file\" ? filePageMeta(page) : extractPageMeta(page.absFile)\n return { params: toParams(page.segments), ...meta }\n}\n\n/** Static spec of an API route: path params and a per-method request/response sketch. */\nexport function extractApiSpec(api: ApiRoute): ApiSpec {\n const params = toParams(api.segments)\n let src: string\n try {\n src = stripComments(fs.readFileSync(api.absFile, \"utf8\"))\n } catch {\n return { params, methods: [] }\n }\n const regions = methodRegions(src)\n const methods = API_METHODS.filter((m) => regions.has(m)).map((method) => {\n const region = regions.get(method)!\n const body = extractBody(region)\n return {\n method,\n query: extractQuery(region),\n ...(body ? { body } : {}),\n responses: extractResponses(region),\n errors: extractErrors(region),\n }\n })\n return { params, methods }\n}\n"]}
1
+ {"version":3,"sources":["../src/core/source.ts","../src/core/route-spec.ts"],"names":[],"mappings":"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","file":"chunk-JKLLFVEU.js","sourcesContent":["/**\n * Low-level source scanning helpers shared by the static analyzers\n * (`diagnostics.ts`, `route-spec.ts`). Everything here is text-only — user code\n * is never imported or executed.\n */\n\n/**\n * Strip block and line comments so commented-out code doesn't produce false\n * positives (or hide real issues). Naive but adequate for presence checks:\n * URLs (`http://`) are preserved via the `[^:]` guard before `//`.\n */\nexport function stripComments(src: string): string {\n return src.replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\").replace(/(^|[^:\"'`\\\\])\\/\\/[^\\n]*/g, \"$1\")\n}\n\nconst OPENERS: Record<string, string> = { \"(\": \")\", \"[\": \"]\", \"{\": \"}\" }\n\n/**\n * Read a balanced bracketed region starting at `openIndex` (which must hold\n * `(`, `[` or `{`), returning the raw text *between* the brackets. Quote- and\n * nesting-aware so brackets inside string literals don't unbalance the scan.\n * Returns null when the region is never closed.\n */\nexport function readBracketBody(src: string, openIndex: number): string | null {\n const open = src[openIndex]\n if (!open || !OPENERS[open]) return null\n let depth = 0\n let quote: string | null = null\n for (let i = openIndex; i < src.length; i++) {\n const c = src[i]!\n if (quote) {\n if (c === \"\\\\\") i++\n else if (c === quote) quote = null\n continue\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n quote = c\n continue\n }\n if (OPENERS[c]) depth++\n else if (c === \")\" || c === \"]\" || c === \"}\") {\n if (--depth === 0) return src.slice(openIndex + 1, i)\n }\n }\n return null\n}\n\n/**\n * Split an argument list or object body on top-level commas, ignoring commas\n * nested inside brackets or string literals. Empty parts are dropped.\n */\nexport function splitTopLevel(body: string): string[] {\n const parts: string[] = []\n let depth = 0\n let quote: string | null = null\n let start = 0\n for (let i = 0; i < body.length; i++) {\n const c = body[i]!\n if (quote) {\n if (c === \"\\\\\") i++\n else if (c === quote) quote = null\n continue\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n quote = c\n continue\n }\n if (OPENERS[c]) depth++\n else if (c === \")\" || c === \"]\" || c === \"}\") depth--\n else if (c === \",\" && depth === 0) {\n parts.push(body.slice(start, i))\n start = i + 1\n }\n }\n parts.push(body.slice(start))\n return parts.map((p) => p.trim()).filter(Boolean)\n}\n\n/**\n * Lex a module just far enough to tell code, comments and string literals\n * apart, *without* moving anything: comments come back blanked to spaces\n * (newlines kept) so offsets and line numbers still match the raw file, and\n * `inString[i]` is 1 where offset `i` sits inside a `'…'`, `\"…\"` or `` `…` ``\n * literal (template `${…}` bodies count as string — close enough for literal\n * scans). `'`/`\"` literals end at a newline, so an apostrophe in JSX text\n * cannot swallow more than the rest of its own line.\n */\nexport function maskSource(src: string): { code: string; inString: Uint8Array } {\n const out = src.split(\"\")\n const inString = new Uint8Array(src.length)\n let i = 0\n while (i < src.length) {\n const c = src[i]!\n const next = src[i + 1]\n if (c === \"/\" && next === \"/\" && src[i - 1] !== \":\") {\n while (i < src.length && src[i] !== \"\\n\") out[i++] = \" \"\n continue\n }\n if (c === \"/\" && next === \"*\") {\n const end = src.indexOf(\"*/\", i + 2)\n const stop = end === -1 ? src.length : end + 2\n for (; i < stop; i++) if (src[i] !== \"\\n\") out[i] = \" \"\n continue\n }\n if (c === '\"' || c === \"'\" || c === \"`\") {\n i++\n while (i < src.length) {\n const d = src[i]!\n if (d === \"\\\\\") {\n inString[i] = 1\n inString[i + 1] = 1\n i += 2\n continue\n }\n if (d === c) break\n if (d === \"\\n\" && c !== \"`\") break\n inString[i++] = 1\n }\n i++\n continue\n }\n i++\n }\n return { code: out.join(\"\"), inString }\n}\n","import fs from \"node:fs\"\nimport { API_METHODS, type ApiMethod } from \"./constants\"\nimport { filePageMeta } from \"./file-page\"\nimport { extractPageMeta } from \"./route-tree\"\nimport { readBracketBody, splitTopLevel, stripComments } from \"./source\"\nimport type { ApiRoute, PageRoute, Segment } from \"./types\"\n\n/**\n * Best-effort *static* specs for the routes `vantage routes --detail` prints:\n * what a page takes in its path, and what an API route accepts and answers\n * with. Like `diagnostics.ts` this only reads text — user modules are never\n * imported or executed — so every field is a hint derived from the source, not\n * a guarantee. Callers pass sources through {@link stripComments} first.\n */\n\n/** A dynamic segment of a route path. */\nexport interface RouteParam {\n name: string\n kind: \"param\" | \"catchall\"\n}\n\n/** Static spec of a page route. */\nexport interface PageSpec {\n params: RouteParam[]\n title?: string\n description?: string\n navLabel?: string\n}\n\n/** The request body an API handler reads, when detectable. */\nexport interface ApiBodySpec {\n format: \"json\" | \"formData\" | \"text\" | \"binary\"\n /** Top-level keys, when the handler destructures them. */\n keys?: string[]\n}\n\n/** A response an API handler returns. */\nexport interface ApiResponseSpec {\n status: number\n /** Response content type, omitted when it can't be derived. */\n contentType?: string\n /** Top-level keys, when the payload is an object literal. */\n keys?: string[]\n}\n\n/** An `HttpError` an API handler throws. */\nexport interface ApiErrorSpec {\n status: number\n message?: string\n}\n\n/** Static spec of one HTTP method handler. */\nexport interface ApiMethodSpec {\n method: ApiMethod\n /** Query string keys read via `searchParams`. */\n query: string[]\n body?: ApiBodySpec\n responses: ApiResponseSpec[]\n errors: ApiErrorSpec[]\n}\n\n/** Static spec of an API route module. */\nexport interface ApiSpec {\n params: RouteParam[]\n methods: ApiMethodSpec[]\n}\n\n/**\n * HTTP method handlers a module exports (direct declaration or export list).\n * `src` must be comment-stripped. Shared with the `INVALID_API_EXPORT`\n * diagnostic so both agree on what counts as a handler.\n */\nexport function detectApiMethods(src: string): ApiMethod[] {\n return API_METHODS.filter(\n (m) =>\n new RegExp(`export\\\\s+(async\\\\s+)?(function|const|let|var)\\\\s+${m}\\\\b`).test(src) ||\n new RegExp(`export\\\\s*\\\\{[^}]*\\\\b${m}\\\\b[^}]*\\\\}`).test(src),\n )\n}\n\n/**\n * Slice the module into one source region per handler: from a handler's export\n * declaration to the next one (or EOF). Coarse on purpose — matching a function\n * body would have to cope with arrow bodies, overloads and trailing helpers —\n * and good enough because handlers are written one after another. A method\n * exported only through an export list gets the whole module as its region.\n */\nfunction methodRegions(src: string): Map<ApiMethod, string> {\n const re = new RegExp(\n `export\\\\s+(?:async\\\\s+)?(?:function|const|let|var)\\\\s+(${API_METHODS.join(\"|\")})\\\\b`,\n \"g\",\n )\n const decls: { method: ApiMethod; index: number }[] = []\n let m: RegExpExecArray | null\n while ((m = re.exec(src))) decls.push({ method: m[1] as ApiMethod, index: m.index })\n\n const regions = new Map<ApiMethod, string>()\n decls.forEach((d, i) => {\n regions.set(d.method, src.slice(d.index, decls[i + 1]?.index ?? src.length))\n })\n for (const method of detectApiMethods(src)) {\n if (!regions.has(method)) regions.set(method, src)\n }\n return regions\n}\n\n/** Top-level keys of an object literal expression, `…` standing in for spreads. */\nfunction objectKeys(expr: string): string[] | undefined {\n const trimmed = expr.trim()\n if (!trimmed.startsWith(\"{\")) return undefined\n const body = readBracketBody(trimmed, 0)\n if (body == null) return undefined\n const keys: string[] = []\n for (const part of splitTopLevel(body)) {\n if (part.startsWith(\"...\")) {\n keys.push(\"…\")\n continue\n }\n const name = part.match(/^[\"'`]?([A-Za-z0-9_$]+)/)\n if (name) keys.push(name[1]!)\n }\n return keys.length ? keys : undefined\n}\n\n/** Read `status: 201` out of a `ResponseInit` literal. */\nfunction initStatus(init: string | undefined): number | undefined {\n const m = init?.match(/\\bstatus\\s*:\\s*(\\d{3})\\b/)\n return m ? Number(m[1]) : undefined\n}\n\n/** Read a content-type header out of a `ResponseInit` literal. */\nfunction initContentType(init: string | undefined): string | undefined {\n const m = init?.match(/[\"']?content-type[\"']?\\s*:\\s*[\"']([^\"';]+)/i)\n return m ? m[1]!.trim() : undefined\n}\n\n/** Every `Response.json(…)` / `json(…)` / `new Response(…)` in a handler region. */\nfunction extractResponses(region: string): ApiResponseSpec[] {\n const re = /\\bnew\\s+Response\\s*\\(|\\bResponse\\s*\\.\\s*json\\s*\\(|(?<![.\\w$])json\\s*\\(/g\n const out: ApiResponseSpec[] = []\n const seen = new Set<string>()\n let m: RegExpExecArray | null\n while ((m = re.exec(region))) {\n const isJson = !m[0].startsWith(\"new\")\n const args = readBracketBody(region, m.index + m[0].length - 1)\n if (args == null) continue\n const [payload, init] = splitTopLevel(args)\n const contentType = isJson\n ? \"application/json\"\n : (initContentType(init) ??\n (payload?.startsWith(\"JSON.stringify\") ? \"application/json\" : undefined))\n const keys = isJson && payload ? objectKeys(payload) : undefined\n const spec: ApiResponseSpec = {\n status: initStatus(init) ?? 200,\n ...(contentType ? { contentType } : {}),\n ...(keys ? { keys } : {}),\n }\n const key = JSON.stringify(spec)\n if (seen.has(key)) continue\n seen.add(key)\n out.push(spec)\n }\n return out\n}\n\n/** Every `new HttpError(status, \"message\")` in a handler region. */\nfunction extractErrors(region: string): ApiErrorSpec[] {\n const re = /\\bHttpError\\s*\\(\\s*(\\d{3})\\s*(?:,\\s*(?:([\"'`])((?:\\\\.|(?!\\2)[\\s\\S])*)\\2))?/g\n const out: ApiErrorSpec[] = []\n const seen = new Set<string>()\n let m: RegExpExecArray | null\n while ((m = re.exec(region))) {\n const message = m[3]?.replace(/\\\\(.)/g, \"$1\")\n const spec: ApiErrorSpec = { status: Number(m[1]), ...(message ? { message } : {}) }\n const key = JSON.stringify(spec)\n if (seen.has(key)) continue\n seen.add(key)\n out.push(spec)\n }\n return out\n}\n\n/** Query keys read through `searchParams.get/getAll/has(\"x\")`. */\nfunction extractQuery(region: string): string[] {\n const re = /searchParams\\s*\\.\\s*(?:get|getAll|has)\\s*\\(\\s*[\"'`]([^\"'`]+)[\"'`]/g\n const out = new Set<string>()\n let m: RegExpExecArray | null\n while ((m = re.exec(region))) out.add(m[1]!)\n return [...out]\n}\n\n/** The request body a handler reads, plus destructured keys when present. */\nfunction extractBody(region: string): ApiBodySpec | undefined {\n const read = region.match(/\\b(?:request|req)\\s*\\.\\s*(json|formData|text|arrayBuffer|blob)\\s*\\(/)\n if (!read) return undefined\n const method = read[1]!\n const format: ApiBodySpec[\"format\"] =\n method === \"json\" || method === \"formData\" || method === \"text\" ? method : \"binary\"\n\n const destructured = region.match(\n /(?:const|let|var)\\s*(\\{[\\s\\S]*?\\})\\s*=\\s*(?:await\\s+)?[\\w.]*\\.json\\s*\\(/,\n )\n const keys = format === \"json\" && destructured ? objectKeys(destructured[1]!) : undefined\n return { format, ...(keys ? { keys } : {}) }\n}\n\nfunction toParams(segments: Segment[]): RouteParam[] {\n const out: RouteParam[] = []\n for (const s of segments) {\n if (s.kind === \"static\") continue\n out.push({ name: s.name, kind: s.kind })\n }\n return out\n}\n\n/** Static spec of a page: its path params and its `definePage` metadata. */\nexport function extractPageSpec(page: PageRoute): PageSpec {\n // A data file has no `definePage` to read, and reading it as UTF-8 would\n // decode megabytes of spreadsheet binary; its title is derived from the name.\n const meta = page.kind === \"file\" ? filePageMeta(page) : extractPageMeta(page.absFile)\n return { params: toParams(page.segments), ...meta }\n}\n\n/** Static spec of an API route: path params and a per-method request/response sketch. */\nexport function extractApiSpec(api: ApiRoute): ApiSpec {\n const params = toParams(api.segments)\n let src: string\n try {\n src = stripComments(fs.readFileSync(api.absFile, \"utf8\"))\n } catch {\n return { params, methods: [] }\n }\n const regions = methodRegions(src)\n const methods = API_METHODS.filter((m) => regions.has(m)).map((method) => {\n const region = regions.get(method)!\n const body = extractBody(region)\n return {\n method,\n query: extractQuery(region),\n ...(body ? { body } : {}),\n responses: extractResponses(region),\n errors: extractErrors(region),\n }\n })\n return { params, methods }\n}\n"]}