@squadbase/vantage 0.0.1 → 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 (167) hide show
  1. package/README.md +21 -4
  2. package/dist/chunk-A2UUGASH.js +12 -0
  3. package/dist/chunk-A2UUGASH.js.map +1 -0
  4. package/dist/{chunk-ZGDU5YLH.js → chunk-PRFBSQA4.js} +9 -5
  5. package/dist/chunk-PRFBSQA4.js.map +1 -0
  6. package/dist/chunk-WQZYXXQW.js +2249 -0
  7. package/dist/chunk-WQZYXXQW.js.map +1 -0
  8. package/dist/{chunk-73J5ZD4C.js → chunk-YLAB6UQS.js} +17 -3
  9. package/dist/chunk-YLAB6UQS.js.map +1 -0
  10. package/dist/cli.js +607 -36
  11. package/dist/cli.js.map +1 -1
  12. package/dist/components/index.d.ts +554 -0
  13. package/dist/components/index.js +1983 -0
  14. package/dist/components/index.js.map +1 -0
  15. package/dist/markdown/index.d.ts +9 -0
  16. package/dist/markdown/index.js +26 -0
  17. package/dist/markdown/index.js.map +1 -0
  18. package/dist/query/index.d.ts +27 -1
  19. package/dist/query/index.js +18 -0
  20. package/dist/query/index.js.map +1 -1
  21. package/dist/server/node.js +6 -1
  22. package/dist/server/node.js.map +1 -1
  23. package/dist/ui/index.d.ts +316 -200
  24. package/dist/ui/index.js +566 -511
  25. package/dist/ui/index.js.map +1 -1
  26. package/dist/vite/index.js +2 -2
  27. package/docs/en/agent-skills.md +89 -0
  28. package/docs/en/api-and-server.md +84 -0
  29. package/docs/en/build-and-deploy.md +280 -0
  30. package/docs/en/cli-reference.md +311 -0
  31. package/docs/en/components.md +233 -0
  32. package/docs/en/data-fetching.md +78 -0
  33. package/docs/en/environment-variables.md +69 -0
  34. package/docs/en/getting-started.md +95 -0
  35. package/docs/en/index.md +65 -0
  36. package/docs/en/markdown/markdown-renderer.md +44 -0
  37. package/docs/en/pages-and-metadata.md +56 -0
  38. package/docs/en/parts/app-shell.md +54 -0
  39. package/docs/en/parts/dashboard-card.md +64 -0
  40. package/docs/en/parts/data-table.md +86 -0
  41. package/docs/en/parts/date-range-picker.md +41 -0
  42. package/docs/en/parts/echart.md +53 -0
  43. package/docs/en/parts/filter-bar.md +66 -0
  44. package/docs/en/parts/funnel-steps.md +33 -0
  45. package/docs/en/parts/metric-value.md +23 -0
  46. package/docs/en/parts/multi-select.md +27 -0
  47. package/docs/en/parts/page-shell.md +45 -0
  48. package/docs/en/parts/searchable-select.md +47 -0
  49. package/docs/en/parts/section-header.md +20 -0
  50. package/docs/en/parts/segmented-control.md +25 -0
  51. package/docs/en/parts/status-badge.md +44 -0
  52. package/docs/en/parts/trend-indicator.md +26 -0
  53. package/docs/en/routing.md +95 -0
  54. package/docs/en/ui/accordion.md +36 -0
  55. package/docs/en/ui/alert.md +31 -0
  56. package/docs/en/ui/badge.md +23 -0
  57. package/docs/en/ui/breadcrumb.md +38 -0
  58. package/docs/en/ui/button.md +39 -0
  59. package/docs/en/ui/calendar.md +28 -0
  60. package/docs/en/ui/card.md +35 -0
  61. package/docs/en/ui/checkbox.md +34 -0
  62. package/docs/en/ui/cn.md +34 -0
  63. package/docs/en/ui/collapsible.md +21 -0
  64. package/docs/en/ui/command.md +37 -0
  65. package/docs/en/ui/dialog.md +52 -0
  66. package/docs/en/ui/dropdown-menu.md +50 -0
  67. package/docs/en/ui/empty.md +22 -0
  68. package/docs/en/ui/error-state.md +50 -0
  69. package/docs/en/ui/input-group.md +30 -0
  70. package/docs/en/ui/input.md +28 -0
  71. package/docs/en/ui/label.md +19 -0
  72. package/docs/en/ui/loading.md +19 -0
  73. package/docs/en/ui/popover.md +34 -0
  74. package/docs/en/ui/progress.md +28 -0
  75. package/docs/en/ui/scroll-area.md +23 -0
  76. package/docs/en/ui/select.md +63 -0
  77. package/docs/en/ui/separator.md +15 -0
  78. package/docs/en/ui/sheet.md +28 -0
  79. package/docs/en/ui/sidebar.md +92 -0
  80. package/docs/en/ui/skeleton.md +16 -0
  81. package/docs/en/ui/slider.md +21 -0
  82. package/docs/en/ui/spinner.md +14 -0
  83. package/docs/en/ui/switch.md +19 -0
  84. package/docs/en/ui/table.md +24 -0
  85. package/docs/en/ui/tabs.md +23 -0
  86. package/docs/en/ui/textarea.md +15 -0
  87. package/docs/en/ui/toggle-group.md +26 -0
  88. package/docs/en/ui/toggle.md +19 -0
  89. package/docs/en/ui/tooltip.md +22 -0
  90. package/docs/en/ui/use-is-mobile.md +18 -0
  91. package/docs/en/ui-and-theming.md +95 -0
  92. package/docs/index.json +1306 -0
  93. package/docs/ja/agent-skills.md +87 -0
  94. package/docs/ja/api-and-server.md +84 -0
  95. package/docs/ja/build-and-deploy.md +279 -0
  96. package/docs/ja/cli-reference.md +305 -0
  97. package/docs/ja/components.md +229 -0
  98. package/docs/ja/data-fetching.md +78 -0
  99. package/docs/ja/environment-variables.md +68 -0
  100. package/docs/ja/getting-started.md +95 -0
  101. package/docs/ja/index.md +65 -0
  102. package/docs/ja/markdown/markdown-renderer.md +43 -0
  103. package/docs/ja/pages-and-metadata.md +56 -0
  104. package/docs/ja/parts/app-shell.md +54 -0
  105. package/docs/ja/parts/dashboard-card.md +64 -0
  106. package/docs/ja/parts/data-table.md +85 -0
  107. package/docs/ja/parts/date-range-picker.md +41 -0
  108. package/docs/ja/parts/echart.md +52 -0
  109. package/docs/ja/parts/filter-bar.md +64 -0
  110. package/docs/ja/parts/funnel-steps.md +33 -0
  111. package/docs/ja/parts/metric-value.md +25 -0
  112. package/docs/ja/parts/multi-select.md +27 -0
  113. package/docs/ja/parts/page-shell.md +45 -0
  114. package/docs/ja/parts/searchable-select.md +47 -0
  115. package/docs/ja/parts/section-header.md +20 -0
  116. package/docs/ja/parts/segmented-control.md +26 -0
  117. package/docs/ja/parts/status-badge.md +44 -0
  118. package/docs/ja/parts/trend-indicator.md +26 -0
  119. package/docs/ja/routing.md +95 -0
  120. package/docs/ja/ui/accordion.md +37 -0
  121. package/docs/ja/ui/alert.md +31 -0
  122. package/docs/ja/ui/badge.md +23 -0
  123. package/docs/ja/ui/breadcrumb.md +38 -0
  124. package/docs/ja/ui/button.md +39 -0
  125. package/docs/ja/ui/calendar.md +28 -0
  126. package/docs/ja/ui/card.md +35 -0
  127. package/docs/ja/ui/checkbox.md +34 -0
  128. package/docs/ja/ui/cn.md +34 -0
  129. package/docs/ja/ui/collapsible.md +21 -0
  130. package/docs/ja/ui/command.md +36 -0
  131. package/docs/ja/ui/dialog.md +51 -0
  132. package/docs/ja/ui/dropdown-menu.md +50 -0
  133. package/docs/ja/ui/empty.md +22 -0
  134. package/docs/ja/ui/error-state.md +50 -0
  135. package/docs/ja/ui/input-group.md +30 -0
  136. package/docs/ja/ui/input.md +28 -0
  137. package/docs/ja/ui/label.md +19 -0
  138. package/docs/ja/ui/loading.md +19 -0
  139. package/docs/ja/ui/popover.md +34 -0
  140. package/docs/ja/ui/progress.md +28 -0
  141. package/docs/ja/ui/scroll-area.md +24 -0
  142. package/docs/ja/ui/select.md +64 -0
  143. package/docs/ja/ui/separator.md +15 -0
  144. package/docs/ja/ui/sheet.md +28 -0
  145. package/docs/ja/ui/sidebar.md +92 -0
  146. package/docs/ja/ui/skeleton.md +16 -0
  147. package/docs/ja/ui/slider.md +21 -0
  148. package/docs/ja/ui/spinner.md +14 -0
  149. package/docs/ja/ui/switch.md +19 -0
  150. package/docs/ja/ui/table.md +24 -0
  151. package/docs/ja/ui/tabs.md +24 -0
  152. package/docs/ja/ui/textarea.md +15 -0
  153. package/docs/ja/ui/toggle-group.md +27 -0
  154. package/docs/ja/ui/toggle.md +19 -0
  155. package/docs/ja/ui/tooltip.md +22 -0
  156. package/docs/ja/ui/use-is-mobile.md +18 -0
  157. package/docs/ja/ui-and-theming.md +95 -0
  158. package/package.json +29 -4
  159. package/registry/blocks/sales-overview.tsx +43 -19
  160. package/registry/ui/data-table.tsx +702 -102
  161. package/skills/vantage-add-feature/SKILL.md +150 -0
  162. package/skills/vantage-app/SKILL.md +258 -0
  163. package/skills/vantage-pitfalls/SKILL.md +115 -0
  164. package/templates/AGENTS.md +268 -0
  165. package/theme.css +178 -40
  166. package/dist/chunk-73J5ZD4C.js.map +0 -1
  167. package/dist/chunk-ZGDU5YLH.js.map +0 -1
@@ -0,0 +1,150 @@
1
+ ---
2
+ name: vantage-add-feature
3
+ description: 既存の Vantage(@squadbase/vantage)アプリにページ・API エンドポイント・UI コンポーネント・ブロックを追加する定型ワークフロー。vantage add コマンドで雛形を出し、規約(動的パラメータの綴り・server 境界・大文字メソッド)を守り、vantage check / routes で確認するまでの一連。Vantage アプリに機能を1つ足すときに使う。
4
+ ---
5
+
6
+ # Vantage への機能追加(page / api / ui / block)
7
+
8
+ 既存の Vantage アプリに要素を1つ追加するときの定型手順。アプリの新規作成や fullstack 化の
9
+ 全体像は `vantage-app` スキル、実装で踏みやすい罠は `vantage-pitfalls` スキルを参照。
10
+
11
+ CLI は雛形を出すだけで、規約の遵守はこちらの責任。**追加したら必ず `vantage check` と
12
+ `vantage routes` で確認する**。
13
+
14
+ ## `vantage add` の4種別
15
+
16
+ ```bash
17
+ vantage add page <name> # ルートページ(.tsx)
18
+ vantage add api <name> # server/api/**.ts の API モジュール
19
+ vantage add ui <name> # registry から UI ソースを components/ui/ にコピー
20
+ vantage add block <name> # registry からブロックを components/blocks/ にコピー
21
+ ```
22
+
23
+ `--force` で既存ファイルを上書き。`add` の雛形は import に `@squadbase/vantage` を焼き込む。
24
+
25
+ ## ページを追加する
26
+
27
+ ```bash
28
+ vantage add page monthly-analysis # → ./monthly-analysis.tsx → ルート /monthly-analysis
29
+ vantage add page sales/index # → ./sales/index.tsx → ルート /sales
30
+ vantage add page "sales/[customerId]" # → 動的ルート /sales/:customerId
31
+ ```
32
+
33
+ 出力される雛形は `definePage` + デフォルトエクスポートのコンポーネント。ファイル名の規約:
34
+
35
+ | ファイル | ルート | 用途 |
36
+ | --- | --- | --- |
37
+ | `foo.tsx` | `/foo` | 通常ページ |
38
+ | `foo/index.tsx` | `/foo` | ディレクトリのインデックス |
39
+ | `foo/[id].tsx` | `/foo/:id` | 動的パラメータ |
40
+ | `_layout.tsx` | — | 配下を包むネストレイアウト(`Outlet` を描く) |
41
+ | `_404.tsx` / `_error.tsx` | — | Not Found / エラーページ |
42
+
43
+ チェックリスト:
44
+
45
+ 1. **デフォルトエクスポートは必須**(無いと `MISSING_DEFAULT_EXPORT` エラー)。
46
+ 2. `definePage({ title })` の値は**リテラル**で書く(ビルド時に静的抽出されるため)。
47
+ 3. 動的ページなら **3つの綴りを同期**:
48
+ - ファイル `foo/[id].tsx`
49
+ - 表示ルート `/foo/:id`
50
+ - リンク `to="/foo/$id"` + `params={{ id }}`、取り出しは `const { id } = useParams()`
51
+ 4. `components/`・`hooks/`・`lib/`・`server/`・`public/` はページにならない(ルート走査対象外)。
52
+
53
+ ## API を追加する(fullstack)
54
+
55
+ ```bash
56
+ vantage add api monthly-analysis # → server/api/monthly-analysis.ts → GET /api/monthly-analysis
57
+ vantage add api "customers/[id]" # → server/api/customers/[id].ts → GET /api/customers/:id
58
+ ```
59
+
60
+ **`server/` を作った時点でアプリは fullstack モードに切り替わる**(SPA → fullstack)。
61
+ 雛形は `GET` ハンドラ:
62
+
63
+ ```ts
64
+ import type { ApiContext } from "@squadbase/vantage/server"
65
+
66
+ export async function GET({ request, params }: ApiContext) {
67
+ return Response.json({ ok: true, params })
68
+ }
69
+ ```
70
+
71
+ チェックリスト:
72
+
73
+ 1. **エクスポート名は大文字の HTTP メソッド**のみ(`GET`/`POST`/`PUT`/`PATCH`/`DELETE`/`OPTIONS`)。
74
+ 他の名前は `INVALID_API_EXPORT` エラー。
75
+ 2. `ApiContext` は Web 標準ベース: `request`(`Request`)・`params`・`env`・`waitUntil`。
76
+ 3. 動的パラメータは `params.id`。`noUncheckedIndexedAccess` が効くので `params.id!` などで narrowing。
77
+ 4. **クライアントに見せるエラーは `HttpError(status, msg)` を throw**。それ以外の throw は
78
+ 500 に丸められログに出る。
79
+ ```ts
80
+ import { HttpError, type ApiContext } from "@squadbase/vantage/server"
81
+ export async function GET({ params }: ApiContext) {
82
+ const row = findCustomer(params.id!)
83
+ if (!row) throw new HttpError(404, `Customer ${params.id} not found`)
84
+ return Response.json(row)
85
+ }
86
+ ```
87
+ 5. **シークレットは `ctx.env` からのみ**読む。クライアントには決して渡さない。
88
+ 6. サーバー専用の共有コードは `server/utils.ts` 等に置く。**クライアントから `server/` を
89
+ import しない**(`CLIENT_IMPORTS_SERVER` エラー。共有は `lib/` に置く)。
90
+
91
+ クライアント側から叩くときは `apiFetch`:
92
+
93
+ ```tsx
94
+ import { apiFetch, useQuery } from "@squadbase/vantage/query"
95
+ const q = useQuery({
96
+ queryKey: ["customer", id],
97
+ queryFn: async () => {
98
+ const res = await apiFetch(`/api/customers/${id}`)
99
+ if (!res.ok) throw new Error("not found")
100
+ return res.json()
101
+ },
102
+ })
103
+ ```
104
+
105
+ ## UI コンポーネント / ブロックを追加する
106
+
107
+ ```bash
108
+ vantage add ui data-table # → components/ui/data-table.tsx(編集可能なソースをコピー)
109
+ vantage add block sales-overview # → components/blocks/sales-overview.tsx
110
+ ```
111
+
112
+ - **多くの UI コンポーネントは「import 専用」で、`add ui` でコピーできる名前は限られる。**
113
+ 現在 registry からコピー可能なのは `data-table`(ui)と `sales-overview`(block)のみ。
114
+ 未知の名前を渡すと利用可能な名前一覧を出して失敗する。
115
+ - コピーが要らない大半のプリミティブは `@squadbase/vantage/ui` から、複合パーツは
116
+ `@squadbase/vantage/components` から**そのまま import**するのが基本(コピー不要)。
117
+ ```tsx
118
+ import { Button, Loading, ErrorState } from "@squadbase/vantage/ui"
119
+ import { DashboardCardPreset, DataTablePreset, EChart } from "@squadbase/vantage/components"
120
+ ```
121
+ - コピーしたソースの相対 import は**ランタイムの `.js` 指定子**で書く(`./cn.js` など)。
122
+ - **props は `vantage docs <name>` で確認してから書く**(`vantage docs button`・
123
+ `vantage docs parts/data-table`。名前が分からなければ `vantage search <やりたいこと>` で
124
+ 探してから `vantage docs` に渡す)。
125
+
126
+ ## 追加後に必ず確認する
127
+
128
+ ```bash
129
+ vantage routes # 追加したページ/API が意図した URL に出ているか
130
+ vantage check # 規約違反が無いか(exit 1 ならエラーあり)
131
+ vantage dev # 実際に動くか(console は開発ターミナルに [browser:…] で転送)
132
+ ```
133
+
134
+ エージェントで自動処理するなら機械可読出力を使う:
135
+
136
+ ```bash
137
+ vantage routes --json # { pages, layouts, notFound, error, apis, hasServer }
138
+ vantage check --json # { ok, errorCount, warningCount, diagnostics[] }
139
+ ```
140
+
141
+ `vantage check` が出しうる診断コード(参考):
142
+
143
+ | code | 意味 |
144
+ | --- | --- |
145
+ | `FORBIDDEN_FILE` | `vite.config.*` などの禁止設定ファイルがある |
146
+ | `ROUTE_CONFLICT` | 同じルートを指すファイルが複数ある |
147
+ | `MISSING_DEFAULT_EXPORT` | ページ/レイアウトにデフォルトエクスポートが無い |
148
+ | `INVALID_API_EXPORT` | API モジュールが大文字メソッド以外をエクスポート |
149
+ | `CLIENT_IMPORTS_SERVER` | クライアントが `server/` を import している |
150
+ | `PUBLIC_ENV_MISUSE`(warn) | `PUBLIC_*` 以外の env をクライアントで読んでいる |
@@ -0,0 +1,258 @@
1
+ ---
2
+ name: vantage-app
3
+ description: Vantage(@squadbase/vantage)ダッシュボードアプリの新規作成と、index.tsx 1ファイルの SPA から server/api・ネストレイアウト・動的ルート・404/error を備えた fullstack への拡張手順。Vantage アプリを一から作る/構成を広げるとき、ルーティング規約や definePage の書き方に迷ったときに使う。
4
+ ---
5
+
6
+ # Vantage アプリの作成と fullstack 拡張
7
+
8
+ Vantage は設定ファイル不要(config-free)な React ダッシュボードフレームワーク。アプリ作者が
9
+ 書くのは `index.tsx`(と任意の追加ファイル)だけで、Vite・ルーティング・TanStack Query・
10
+ Tailwind・UI キット・開発サーバー・API サーバー・ビルドはすべて Vantage が所有する。
11
+
12
+ このスキルは「アプリを一から作る」「最小の SPA を fullstack に広げる」ワークフローを扱う。
13
+ 個別のページ/API/UI の追加は `vantage-add-feature` スキル、実装中に踏みやすい落とし穴は
14
+ `vantage-pitfalls` スキルを参照。
15
+
16
+ ## 大原則(先に頭に入れる)
17
+
18
+ - **設定ファイルを作らない。** `vite.config.*`・`tailwind.config.*`・`postcss.config.*`・
19
+ `components.json`・`vantage.config.*` はすべて禁止。存在すると `vantage check` がエラーにする。
20
+ 設定は「ファイル名の規約」で表現する。
21
+ - **import は必ず `@squadbase/vantage` のサブパス経由。** `@tanstack/*`・`@base-ui/react`・
22
+ `echarts`・`hono`・`vite`・`tailwindcss` を直接 import しない。`lucide-react` のアイコンだけは
23
+ 直接 import してよい。
24
+ - **`.vantage/` と `dist/` は生成物。** 編集しない・読みにいかない(gitignore 済み)。
25
+ - **モジュール間 import はランタイムの `.js` 指定子を使う**(例: `./components/revenue-chart.js`)。
26
+ ソースは `.tsx`/`.ts` でも、相対 import の拡張子は `.js` と書く。ESM の Node 解決に必要。
27
+
28
+ ## サブパスの地図
29
+
30
+ | import 元 | 提供するもの |
31
+ | --- | --- |
32
+ | `@squadbase/vantage` | `definePage`(ページ設定) |
33
+ | `@squadbase/vantage/router` | `Link`・`Outlet`・`useParams`・`useSearch`・`useNavigate`・`redirect`・`notFound` |
34
+ | `@squadbase/vantage/query` | `useQuery`・`useMutation`・`apiFetch`・`apiUrl` ほか TanStack Query の再エクスポート |
35
+ | `@squadbase/vantage/ui` | shadcn/ui 系プリミティブ(`Button`・`Loading`・`ErrorState`・`Empty` ほか) |
36
+ | `@squadbase/vantage/components` | 複合パーツ(`PageShell`・`DashboardCardPreset`・`DataTablePreset`・`EChart` ほか) |
37
+ | `@squadbase/vantage/markdown` | `MarkdownRenderer`(Shiki を隔離するため専用サブパス) |
38
+ | `@squadbase/vantage/server` | `ApiContext`・`HttpError`(server/ 側でのみ使う) |
39
+
40
+ どのコンポーネントがあるか、props が何かは **`vantage docs` で引く**(パッケージ同梱。
41
+ `vantage docs` で一覧、`vantage docs button` / `vantage docs parts/data-table` で個別ページ、
42
+ `--json` で機械可読)。**props を推測で書かない。** 名前が分からないときは
43
+ **`vantage search <やりたいこと>`**(例: `vantage search 期間を選ぶ UI が欲しい`)で探し、
44
+ 出てきた slug を `vantage docs` に渡す。
45
+
46
+ ## Step 1 — 最小アプリ(SPA モード)
47
+
48
+ `package.json` と `index.tsx` の2ファイルだけで動く。
49
+
50
+ `package.json`:
51
+
52
+ ```json
53
+ {
54
+ "name": "my-dashboard",
55
+ "private": true,
56
+ "type": "module",
57
+ "scripts": {
58
+ "dev": "vantage dev",
59
+ "build": "vantage build",
60
+ "preview": "vantage preview",
61
+ "check": "vantage check",
62
+ "routes": "vantage routes"
63
+ },
64
+ "dependencies": {
65
+ "@squadbase/vantage": "^0.1.0",
66
+ "react": "^19.2.7",
67
+ "react-dom": "^19.2.7"
68
+ }
69
+ }
70
+ ```
71
+
72
+ `index.tsx`(ルートのデフォルトエクスポートが `/` ページになる):
73
+
74
+ ```tsx
75
+ export default function Dashboard() {
76
+ return (
77
+ <main className="p-6">
78
+ <h1 className="text-2xl font-semibold">Dashboard</h1>
79
+ </main>
80
+ )
81
+ }
82
+ ```
83
+
84
+ 起動と検証:
85
+
86
+ ```bash
87
+ pnpm install
88
+ pnpm dev # → http://localhost:5173(HMR)
89
+ pnpm check # 静的診断。エラーがあれば exit 1
90
+ pnpm routes # ページ/API の URL マップ
91
+ ```
92
+
93
+ `server/` ディレクトリが無いので、この時点では **SPA モード**(`vantage-manifest.json` の
94
+ `mode: "spa"`)。
95
+
96
+ ## Step 2 — ページを足してファイルベースルーティングにする
97
+
98
+ ルートディレクトリ直下の `.tsx`/`.jsx` がそのままページになる。規約:
99
+
100
+ | ファイル | ルート |
101
+ | --- | --- |
102
+ | `index.tsx` | `/` |
103
+ | `monthly-analysis.tsx` | `/monthly-analysis` |
104
+ | `sales/index.tsx` | `/sales` |
105
+ | `sales/[customerId].tsx` | `/sales/:customerId`(動的パラメータ) |
106
+ | `_layout.tsx` | そのディレクトリ配下を包むネストレイアウト |
107
+ | `_404.tsx` | Not Found ページ |
108
+ | `_error.tsx` | ルートが throw したときのエラーページ |
109
+
110
+ `components/`・`hooks/`・`lib/`・`server/`・`public/` はルート走査の対象外(ページにならない)。
111
+
112
+ 各ページは **デフォルトエクスポートの React コンポーネントが必須**。タイトル等は `definePage`:
113
+
114
+ ```tsx
115
+ import { definePage } from "@squadbase/vantage"
116
+
117
+ export const page = definePage({ title: "Monthly Analysis" })
118
+
119
+ export default function MonthlyAnalysis() {
120
+ return <main className="p-6">…</main>
121
+ }
122
+ ```
123
+
124
+ > `page` エクスポートはランタイムでは読まれない。title/description はビルド時に**静的抽出**される
125
+ > ので、値はリテラルで書く(変数や関数呼び出しにしない)。
126
+
127
+ ### ネストレイアウトと特殊ページ
128
+
129
+ `_layout.tsx` は `Outlet` で子ルートを描く:
130
+
131
+ ```tsx
132
+ import { Outlet } from "@squadbase/vantage/router"
133
+
134
+ export default function RootLayout() {
135
+ return (
136
+ <div className="min-h-screen">
137
+ <header>…</header>
138
+ <Outlet />
139
+ </div>
140
+ )
141
+ }
142
+ ```
143
+
144
+ `_404.tsx` / `_error.tsx` は `ui/` の状態コンポーネントを使うと早い:
145
+
146
+ ```tsx
147
+ // _error.tsx
148
+ import { ErrorState } from "@squadbase/vantage/ui"
149
+ export default function RouteError({ error }: { error: unknown }) {
150
+ const message = error instanceof Error ? error.message : String(error)
151
+ return <ErrorState title="This page failed to render" message={message} />
152
+ }
153
+ ```
154
+
155
+ ### 動的パラメータの「3つの綴り」を必ず同期させる
156
+
157
+ これは崩すと壊れる不変条件:
158
+
159
+ - ファイル名 `sales/[customerId].tsx`
160
+ - 表示ルート `/sales/:customerId`
161
+ - リンク: `to="/sales/$customerId"` + `params={{ customerId }}`
162
+
163
+ ```tsx
164
+ import { Link, useParams } from "@squadbase/vantage/router"
165
+
166
+ // 一覧側のリンク
167
+ <Link to="/sales/$customerId" params={{ customerId: row.id }}>{row.name}</Link>
168
+
169
+ // 詳細ページ側で取り出す
170
+ const { customerId } = useParams()
171
+ ```
172
+
173
+ ## Step 3 — サーバー状態(API を持たない fetch)
174
+
175
+ 外部 API を叩くだけなら `server/` は不要。`@squadbase/vantage/query` の `useQuery` を使う:
176
+
177
+ ```tsx
178
+ import { useQuery } from "@squadbase/vantage/query"
179
+
180
+ const q = useQuery({ queryKey: ["stats"], queryFn: () => fetch("/…").then((r) => r.json()) })
181
+ if (q.isPending) return <Loading />
182
+ if (q.isError) return <ErrorState message={(q.error as Error).message} />
183
+ ```
184
+
185
+ `QueryClient` は Vantage が1つだけ管理する(staleTime 30s・retry 1・refetchOnWindowFocus false)。
186
+ 挙動を変えたいときはクエリ側のオプションで上書きする(クライアントごと差し替える口は無い)。
187
+
188
+ ## Step 4 — fullstack へ拡張(server/api を足す)
189
+
190
+ **`server/` ディレクトリを作った瞬間に fullstack モードになる。** `server/api/**` の各ファイルが
191
+ API ルートになり、ビルドは client + server バンドル + `mode: "fullstack"` の manifest を出す。
192
+
193
+ `server/api/monthly-analysis.ts` → `GET /api/monthly-analysis`:
194
+
195
+ ```ts
196
+ import type { ApiContext } from "@squadbase/vantage/server"
197
+
198
+ export async function GET(_ctx: ApiContext) {
199
+ return Response.json({ ok: true })
200
+ }
201
+ ```
202
+
203
+ - API モジュールは大文字の HTTP メソッド(`GET`/`POST`/`PUT`/`PATCH`/`DELETE`/`OPTIONS`)を
204
+ エクスポートする。それ以外の名前は `INVALID_API_EXPORT` エラー。
205
+ - 動的 API も `[id].ts` 記法: `server/api/customers/[id].ts` → `GET /api/customers/:id`。
206
+ `params.id` で取り出す(`noUncheckedIndexedAccess` が効くので `params.id!` 等で narrowing)。
207
+ - クライアントに見せたいエラーは `HttpError(status, msg)` を throw する。それ以外の throw は
208
+ ログに記録され汎用の 500 になる。
209
+ - **シークレットは `ApiContext.env` にだけ届く**(クライアントには決して届かない)。
210
+
211
+ クライアント側からは `apiFetch` で同一オリジンの `/api/*` を叩く:
212
+
213
+ ```tsx
214
+ import { apiFetch, useQuery } from "@squadbase/vantage/query"
215
+
216
+ const q = useQuery({
217
+ queryKey: ["customer", customerId],
218
+ queryFn: async () => {
219
+ const res = await apiFetch(`/api/customers/${customerId}`)
220
+ if (!res.ok) throw new Error(`Customer ${customerId} not found`)
221
+ return res.json()
222
+ },
223
+ })
224
+ ```
225
+
226
+ ### server/ とクライアントの境界(絶対に守る)
227
+
228
+ - **クライアントコードは `server/` を import してはならない。** 共有したいコードは `lib/` に置く。
229
+ 違反は `CLIENT_IMPORTS_SERVER` エラー(静的にもビルド時にも弾かれる)。
230
+ - サーバー専用のデータ/ヘルパは `server/utils.ts` などに置き、`server/api/**` からのみ import する。
231
+
232
+ ## Step 5 — 環境変数
233
+
234
+ - **クライアントに届くのは `PUBLIC_` 接頭辞の付いた env のみ**。`import.meta.env.PUBLIC_FOO`。
235
+ `VITE_` 系の API は無い。それ以外を `import.meta.env` で読むと `PUBLIC_ENV_MISUSE` 警告。
236
+ - サーバー側のシークレットは `ApiContext.env.MY_SECRET` で読む(`server/` 内のみ)。
237
+
238
+ ## Step 6 — 検証・ビルド・プレビュー
239
+
240
+ ```bash
241
+ pnpm check # 静的診断(禁止ファイル・ルート衝突・境界・API export・env 誤用)
242
+ pnpm routes # ページ + API の URL マップを確認
243
+ pnpm build # dist/ に client(+ server)+ vantage-manifest.json
244
+ pnpm preview # 本番ビルドをローカル実行(fullstack:4173 / spa は Vite preview)
245
+ ```
246
+
247
+ `vantage-manifest.json` の `mode` は `server/` の有無から自動で決まる(手で書かない)。
248
+ デプロイまでの詳細は別途デプロイ手順を参照。
249
+
250
+ ## 詰まったら
251
+
252
+ - `console.*` とランタイムエラーは開発ターミナルに `[browser:…]` として転送される。
253
+ - `vantage check --json` / `vantage routes --json` はエージェント向けの機械可読出力。
254
+ - 規約や props を確かめたいときは `vantage docs <name>`(ガイドは `vantage docs routing` など、
255
+ 一覧は `vantage docs`)。名前が思い出せないときは `vantage search <やりたいこと>`、
256
+ 綴りを横断で確かめたいときは `vantage search "<regex>" --regex`。
257
+ - Base UI(≠ Radix)固有の罠、`SelectValue` の挙動、EChart のテーマ非追従などは
258
+ `vantage-pitfalls` スキルにまとまっている。
@@ -0,0 +1,115 @@
1
+ ---
2
+ name: vantage-pitfalls
3
+ description: Vantage(@squadbase/vantage)アプリを書くときに静かに壊れる落とし穴のリファレンス。Base UI は Radix ではない(asChild 無し・render プロップ)、SelectValue は value を描く、クライアントから server/ を import しない、EChart はテーマ非追従、相対 import は .js 指定子、動的パラメータの3綴り同期、PUBLIC_ env、HttpError など。Vantage の UI コンポーネントやルーティングが思った通りに動かないとき、ビルドやスタイルが静かに欠落するときに参照する。
4
+ ---
5
+
6
+ # Vantage アプリの落とし穴リファレンス
7
+
8
+ Vantage アプリで「型は通るのに実行時/ビルド時に静かに壊れる」パターン集。症状が出たとき、
9
+ または該当機能を書く前に参照する。アプリ作成の流れは `vantage-app`、機能追加は
10
+ `vantage-add-feature` スキル。個々のコンポーネントの props と使用例は
11
+ `vantage docs <name>`(例: `vantage docs ui/select`)で引ける。
12
+
13
+ ## UI:Base UI は Radix ではない
14
+
15
+ Vantage の UI キットは shadcn/ui の **Base UI バリアント**。Radix の癖で書くと動かない。
16
+
17
+ - **`asChild` は無い。** 代わりに `render` プロップを使う。
18
+ - **`Select` の `onValueChange` は `string | null`** を渡す(null が来うる)。
19
+ - **`Checkbox` の `checked` は `boolean` のみ。** 中間状態は `indeterminate` プロップ。
20
+ - **`ToggleGroup` の `value` は配列。**
21
+
22
+ ### `SelectValue` は「選択中の value」を描く。`SelectItem` の children は見ない
23
+
24
+ 値とラベルが違うと、トリガーに `kanto` や `/sales` といった**生の値**が出てしまう。ラベルを
25
+ 出したいときは `items`(value→label のマップ)を渡す:
26
+
27
+ ```tsx
28
+ <Select items={{ kanto: "関東", kansai: "関西" }} … />
29
+ ```
30
+
31
+ `FilterBarSelect` / `AppShell` の header variant / `DataTablePagination` は内部でこの `items` を
32
+ 組み立てている。自前で `Select` を使うときは忘れやすい。
33
+
34
+ ## チャート:`EChart` はテーマに自動追従しない
35
+
36
+ `EChart` は薄いラッパー(init/resize/dispose・loading・`onEvents`・PNG コピー/ダウンロードだけ)。
37
+ 配色は**各アプリが `option.color`** で決める(または `theme` に echarts テーマを渡す)。
38
+
39
+ - `--chart-1..5` を読んで明暗テーマを自動で組む機構は**意図的に無い**(ECharts はキャンバス
40
+ 描画で CSS 変数を読めない)。ダークモード追従が要るなら自前で色を切り替える。
41
+ - キャンバス系なので、コンテナに高さを与えないと何も見えない。
42
+
43
+ ## 境界:クライアントから `server/` を import しない
44
+
45
+ - クライアント側のどのファイルも `server/` 配下を import してはいけない(`CLIENT_IMPORTS_SERVER`
46
+ エラー。静的にもビルド時にも弾かれる)。**共有したいコードは `lib/` に置く。**
47
+ - シークレット等の env は `ApiContext.env`(server/ 内)にだけ届く。クライアントには渡らない。
48
+
49
+ ## env:クライアントに届くのは `PUBLIC_` 接頭辞だけ
50
+
51
+ - クライアントで読めるのは `import.meta.env.PUBLIC_*` と `MODE`/`DEV`/`PROD`/`SSR`/`BASE_URL` のみ。
52
+ それ以外を `import.meta.env` で読むと `PUBLIC_ENV_MISUSE` 警告。
53
+ - `VITE_` 系の公開 API は無い。サーバーのシークレットは `ctx.env` で読む。
54
+
55
+ ## import:相対 import はランタイムの `.js` 指定子で書く
56
+
57
+ ソースが `.tsx`/`.ts` でも、**モジュール間の相対 import は `.js`** と書く(ESM の Node 解決に必要):
58
+
59
+ ```tsx
60
+ import { RevenueChart } from "./components/revenue-chart.js" // ← .tsx ではなく .js
61
+ ```
62
+
63
+ `@squadbase/vantage` のサブパス import はこの限りではない(`@squadbase/vantage/ui` 等)。
64
+
65
+ ## ルーティング:動的パラメータは「3つの綴り」を同期させる
66
+
67
+ ズレると静かにマッチしなくなる:
68
+
69
+ - ファイル `sales/[customerId].tsx`
70
+ - 表示ルート `/sales/:customerId`
71
+ - リンク `to="/sales/$customerId"` + `params={{ customerId }}` / 取り出し `useParams().customerId`
72
+
73
+ ## ページ:`definePage` の値はリテラルで書く
74
+
75
+ title/description は**ビルド時に静的抽出**される。変数・関数呼び出し・テンプレート補間を使うと
76
+ 抽出できない。`export const page = definePage({ title: "…" })` の右辺はリテラルにする。
77
+ なお `page` エクスポートはランタイムでは読まれない(抽出専用)。
78
+
79
+ ## API:見せたいエラーは `HttpError` を throw する
80
+
81
+ ```ts
82
+ import { HttpError } from "@squadbase/vantage/server"
83
+ throw new HttpError(404, "Not found") // → クライアントに 404 + message
84
+ throw new Error("boom") // → ログに出て汎用 500 に丸められる
85
+ ```
86
+
87
+ `instanceof HttpError` は dev の module realm 跨ぎで壊れることがある。ライブラリ側の判定は
88
+ `isHttpError` を使う設計。アプリ側は素直に `throw new HttpError(...)` すればよい。
89
+
90
+ ## 設定ファイル:作った時点で `vantage check` がエラーにする
91
+
92
+ 以下はすべて禁止(`FORBIDDEN_FILE`)。Vantage が所有しているので置かない:
93
+
94
+ - `vite.config.*` — Vite 設定は Vantage が所有
95
+ - `tailwind.config.*` — テーマは `styles.css` のトークンで調整
96
+ - `postcss.config.*` — Vantage が管理
97
+ - `components.json` — UI は `vantage add ui <name>` でコピー
98
+ - `vantage.config.*` — v1 に設定ファイルは無い(規約で表現)
99
+
100
+ ## Markdown:`MarkdownRenderer` は `@squadbase/vantage/markdown` から
101
+
102
+ Markdown 描画は専用サブパスに隔離されている(Shiki の全言語文法を引き込むため)。
103
+ `components/` から import しない。使うアプリだけが `@squadbase/vantage/markdown` から取り込む。
104
+
105
+ ## テーブル:`ColumnDef<Row>[]` を明示的に注釈する
106
+
107
+ ```tsx
108
+ import { type ColumnDef } from "@squadbase/vantage/components"
109
+ const columns: ColumnDef<Row>[] = [ … ] // 注釈しないと accessorKey が string に広がり弾かれる
110
+ ```
111
+
112
+ ## 生成物:`.vantage/` と `dist/` は触らない
113
+
114
+ どちらも gitignore 済みの生成物。編集しても次のコマンドで上書きされる。`.vantage/` は任意の CLI
115
+ コマンド、または `vantage upgrade` で再生成される。