@squadbase/vantage 0.2.3 → 0.3.1

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 (68) hide show
  1. package/README.md +1 -1
  2. package/dist/add-DWNDA57L.js +6 -0
  3. package/dist/add-DWNDA57L.js.map +1 -0
  4. package/dist/build-QP5BL3K7.js +108 -0
  5. package/dist/build-QP5BL3K7.js.map +1 -0
  6. package/dist/check-QPFERQLO.js +38 -0
  7. package/dist/check-QPFERQLO.js.map +1 -0
  8. package/dist/chunk-2KRCGXBY.js +56 -0
  9. package/dist/chunk-2KRCGXBY.js.map +1 -0
  10. package/dist/chunk-BAM33YZ6.js +202 -0
  11. package/dist/chunk-BAM33YZ6.js.map +1 -0
  12. package/dist/chunk-C4MQRWXS.js +81 -0
  13. package/dist/chunk-C4MQRWXS.js.map +1 -0
  14. package/dist/chunk-D4Y5RWJA.js +189 -0
  15. package/dist/chunk-D4Y5RWJA.js.map +1 -0
  16. package/dist/chunk-OFFJ3OL5.js +121 -0
  17. package/dist/chunk-OFFJ3OL5.js.map +1 -0
  18. package/dist/chunk-PSEYFYAP.js +276 -0
  19. package/dist/chunk-PSEYFYAP.js.map +1 -0
  20. package/dist/chunk-R2YYVCGS.js +345 -0
  21. package/dist/chunk-R2YYVCGS.js.map +1 -0
  22. package/dist/chunk-RA6Z3OX3.js +199 -0
  23. package/dist/chunk-RA6Z3OX3.js.map +1 -0
  24. package/dist/chunk-UTEKU3LG.js +267 -0
  25. package/dist/chunk-UTEKU3LG.js.map +1 -0
  26. package/dist/chunk-VXAV2435.js +142 -0
  27. package/dist/chunk-VXAV2435.js.map +1 -0
  28. package/dist/cli.js +24 -1604
  29. package/dist/cli.js.map +1 -1
  30. package/dist/dev-NX3PMYCT.js +55 -0
  31. package/dist/dev-NX3PMYCT.js.map +1 -0
  32. package/dist/docs-M5ZUI25V.js +114 -0
  33. package/dist/docs-M5ZUI25V.js.map +1 -0
  34. package/dist/doctor-N6JMEWKT.js +58 -0
  35. package/dist/doctor-N6JMEWKT.js.map +1 -0
  36. package/dist/index.d.ts +1 -1
  37. package/dist/index.js +1 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/preview-CLALOEFE.js +49 -0
  40. package/dist/preview-CLALOEFE.js.map +1 -0
  41. package/dist/routes-YI25MKPF.js +119 -0
  42. package/dist/routes-YI25MKPF.js.map +1 -0
  43. package/dist/search-5TYAIQ7C.js +325 -0
  44. package/dist/search-5TYAIQ7C.js.map +1 -0
  45. package/dist/server/node.d.ts +1 -12
  46. package/dist/types-De3mo_hv.d.ts +59 -0
  47. package/dist/upgrade-3P5MBGE6.js +29 -0
  48. package/dist/upgrade-3P5MBGE6.js.map +1 -0
  49. package/dist/vite/index.d.ts +13 -0
  50. package/dist/vite/index.js +5 -1
  51. package/docs/en/changelog.md +82 -0
  52. package/docs/en/cli-reference.md +34 -4
  53. package/docs/en/components.md +2 -1
  54. package/docs/en/getting-started.md +6 -3
  55. package/docs/en/routing.md +27 -1
  56. package/docs/en/ui-and-theming.md +4 -2
  57. package/docs/ja/changelog.md +74 -0
  58. package/docs/ja/cli-reference.md +34 -4
  59. package/docs/ja/components.md +3 -2
  60. package/docs/ja/getting-started.md +6 -3
  61. package/docs/ja/routing.md +26 -1
  62. package/docs/ja/ui-and-theming.md +4 -2
  63. package/package.json +7 -4
  64. package/skills/vantage-add-feature/SKILL.md +14 -5
  65. package/skills/vantage-app/SKILL.md +9 -5
  66. package/templates/AGENTS.md +50 -6
  67. package/dist/chunk-3QKPNWKJ.js +0 -762
  68. package/dist/chunk-3QKPNWKJ.js.map +0 -1
@@ -0,0 +1,189 @@
1
+ import { SRC_DIR, RESERVED_PAGE_NAMES, NON_ROUTE_DIRS, PAGE_EXTENSIONS, API_EXTENSIONS } from './chunk-C4MQRWXS.js';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ // @squadbase/vantage — generated build. Do not edit.
6
+
7
+ // src/core/profile.ts
8
+ var ENABLED = Boolean(process.env.VANTAGE_PROFILE);
9
+ function report(label, ms) {
10
+ const since = (performance.now() / 1e3).toFixed(3);
11
+ process.stderr.write(`[vantage:profile] ${label.padEnd(24)} ${ms.toFixed(1).padStart(8)}ms @${since}s
12
+ `);
13
+ }
14
+ function timed(label, fn) {
15
+ if (!ENABLED) return fn();
16
+ const t0 = performance.now();
17
+ try {
18
+ return fn();
19
+ } finally {
20
+ report(label, performance.now() - t0);
21
+ }
22
+ }
23
+ async function timedAsync(label, fn) {
24
+ if (!ENABLED) return fn();
25
+ const t0 = performance.now();
26
+ try {
27
+ return await fn();
28
+ } finally {
29
+ report(label, performance.now() - t0);
30
+ }
31
+ }
32
+ function toPosix(root, abs) {
33
+ return path.relative(root, abs).split(path.sep).join("/");
34
+ }
35
+ function parseRawSegment(raw) {
36
+ const catchall = raw.match(/^\[\.\.\.(.+)\]$/);
37
+ if (catchall) return { kind: "catchall", name: catchall[1] };
38
+ const param = raw.match(/^\[(.+)\]$/);
39
+ if (param) return { kind: "param", name: param[1] };
40
+ return { kind: "static", value: raw };
41
+ }
42
+ function displayPath(segments) {
43
+ if (segments.length === 0) return "/";
44
+ const parts = segments.map((s) => {
45
+ if (s.kind === "static") return s.value;
46
+ if (s.kind === "param") return `:${s.name}`;
47
+ return "*";
48
+ });
49
+ return "/" + parts.join("/");
50
+ }
51
+ function fileToSegments(relNoExt) {
52
+ const raw = relNoExt.split("/");
53
+ if (raw[raw.length - 1] === "index") raw.pop();
54
+ return raw.map(parseRawSegment);
55
+ }
56
+ function stripExt(file, exts) {
57
+ for (const ext of exts) {
58
+ if (file.endsWith(ext)) return file.slice(0, -ext.length);
59
+ }
60
+ return file;
61
+ }
62
+ function hasExt(file, exts) {
63
+ return exts.some((ext) => file.endsWith(ext));
64
+ }
65
+ function walk(dir, root, out) {
66
+ let entries;
67
+ try {
68
+ entries = fs.readdirSync(dir, { withFileTypes: true });
69
+ } catch {
70
+ return;
71
+ }
72
+ for (const entry of entries) {
73
+ const abs = path.join(dir, entry.name);
74
+ if (entry.isDirectory()) {
75
+ if (NON_ROUTE_DIRS.has(entry.name)) continue;
76
+ if (entry.name.startsWith(".")) continue;
77
+ walk(abs, root, out);
78
+ } else if (entry.isFile()) {
79
+ out.push(abs);
80
+ }
81
+ }
82
+ }
83
+ function resolvePageRoot(root) {
84
+ const abs = path.join(root, SRC_DIR);
85
+ try {
86
+ if (fs.statSync(abs).isDirectory()) return { dir: SRC_DIR, abs };
87
+ } catch {
88
+ }
89
+ return { dir: "", abs: root };
90
+ }
91
+ function scanPages(root, pageRoot) {
92
+ const files = [];
93
+ walk(pageRoot, pageRoot, files);
94
+ const pages = [];
95
+ const layouts = [];
96
+ let notFound = null;
97
+ let error = null;
98
+ for (const abs of files) {
99
+ const file = toPosix(root, abs);
100
+ const rel = toPosix(pageRoot, abs);
101
+ const base = path.posix.basename(rel);
102
+ if (!hasExt(base, PAGE_EXTENSIONS)) continue;
103
+ const nameNoExt = stripExt(base, PAGE_EXTENSIONS);
104
+ const dir = path.posix.dirname(rel);
105
+ const dirPosix = dir === "." ? "" : dir;
106
+ if (nameNoExt === RESERVED_PAGE_NAMES.layout) {
107
+ layouts.push({ file, absFile: abs, dir: dirPosix });
108
+ continue;
109
+ }
110
+ if (nameNoExt === RESERVED_PAGE_NAMES.notFound) {
111
+ if (dirPosix === "") notFound = file;
112
+ continue;
113
+ }
114
+ if (nameNoExt === RESERVED_PAGE_NAMES.error) {
115
+ if (dirPosix === "") error = file;
116
+ continue;
117
+ }
118
+ if (nameNoExt.startsWith("_")) continue;
119
+ const relNoExt = stripExt(rel, PAGE_EXTENSIONS);
120
+ const segments = fileToSegments(relNoExt);
121
+ const routePath = displayPath(segments);
122
+ const dynamic = segments.some((s) => s.kind !== "static");
123
+ pages.push({ file, absFile: abs, routePath, segments, dir: dirPosix, dynamic });
124
+ }
125
+ pages.sort((a, b) => routeSortKey(a).localeCompare(routeSortKey(b)));
126
+ layouts.sort((a, b) => a.dir.localeCompare(b.dir));
127
+ return { pages, layouts, notFound, error };
128
+ }
129
+ function routeSortKey(p) {
130
+ return p.segments.map((s) => s.kind === "static" ? "0" + s.value : s.kind === "param" ? "1" : "2").join("/");
131
+ }
132
+ function scanApis(root) {
133
+ const apiDir = path.join(root, "server", "api");
134
+ if (!fs.existsSync(apiDir)) return [];
135
+ const files = [];
136
+ const walkApi = (dir) => {
137
+ let entries;
138
+ try {
139
+ entries = fs.readdirSync(dir, { withFileTypes: true });
140
+ } catch {
141
+ return;
142
+ }
143
+ for (const entry of entries) {
144
+ const abs = path.join(dir, entry.name);
145
+ if (entry.isDirectory()) walkApi(abs);
146
+ else if (entry.isFile()) files.push(abs);
147
+ }
148
+ };
149
+ walkApi(apiDir);
150
+ const apis = [];
151
+ for (const abs of files) {
152
+ const base = path.basename(abs);
153
+ if (!hasExt(base, API_EXTENSIONS)) continue;
154
+ const nameNoExt = stripExt(base, API_EXTENSIONS);
155
+ if (base.endsWith(".d.ts")) continue;
156
+ if (nameNoExt.startsWith("_")) continue;
157
+ const relFromServer = toPosix(path.join(root, "server"), abs);
158
+ const relNoExt = stripExt(relFromServer, API_EXTENSIONS);
159
+ const withoutApiPrefix = relNoExt.replace(/^api\/?/, "");
160
+ const segTokens = withoutApiPrefix === "" ? [] : withoutApiPrefix.split("/");
161
+ if (segTokens[segTokens.length - 1] === "index") segTokens.pop();
162
+ const segments = segTokens.map(parseRawSegment);
163
+ const routePath = "/api" + (segments.length ? displayPath(segments) : "");
164
+ const params = segments.filter((s) => s.kind !== "static").map((s) => s.name);
165
+ apis.push({
166
+ file: toPosix(root, abs),
167
+ absFile: abs,
168
+ routePath,
169
+ pattern: routePath,
170
+ segments,
171
+ params
172
+ });
173
+ }
174
+ apis.sort((a, b) => a.routePath.localeCompare(b.routePath));
175
+ return apis;
176
+ }
177
+ function scanProject(root) {
178
+ return timed("scan", () => {
179
+ const pageRoot = resolvePageRoot(root);
180
+ const { pages, layouts, notFound, error } = scanPages(root, pageRoot.abs);
181
+ const hasServer = fs.existsSync(path.join(root, "server"));
182
+ const apis = hasServer ? scanApis(root) : [];
183
+ return { pages, layouts, notFound, error, apis, hasServer, pageDir: pageRoot.dir };
184
+ });
185
+ }
186
+
187
+ export { resolvePageRoot, scanProject, timed, timedAsync };
188
+ //# sourceMappingURL=chunk-D4Y5RWJA.js.map
189
+ //# sourceMappingURL=chunk-D4Y5RWJA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/profile.ts","../src/core/scan.ts"],"names":[],"mappings":"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","file":"chunk-D4Y5RWJA.js","sourcesContent":["/**\n * Opt-in phase timing, enabled with `VANTAGE_PROFILE=1`.\n *\n * Every line goes to **stderr**, so turning it on never corrupts the `--json`\n * payloads other commands write to stdout. Disabled, `timed()` is a direct call\n * through — no timers, no allocation.\n */\n\nconst ENABLED = Boolean(process.env.VANTAGE_PROFILE)\n\n/** Whether profiling output is on. Cheap enough to branch on in hot paths. */\nexport const profiling = ENABLED\n\nfunction report(label: string, ms: number): void {\n const since = (performance.now() / 1000).toFixed(3)\n process.stderr.write(`[vantage:profile] ${label.padEnd(24)} ${ms.toFixed(1).padStart(8)}ms @${since}s\\n`)\n}\n\n/** Time a synchronous phase. */\nexport function timed<T>(label: string, fn: () => T): T {\n if (!ENABLED) return fn()\n const t0 = performance.now()\n try {\n return fn()\n } finally {\n report(label, performance.now() - t0)\n }\n}\n\n/** Time an asynchronous phase. */\nexport async function timedAsync<T>(label: string, fn: () => Promise<T>): Promise<T> {\n if (!ENABLED) return fn()\n const t0 = performance.now()\n try {\n return await fn()\n } finally {\n report(label, performance.now() - t0)\n }\n}\n\n/** Record a one-off duration measured by the caller. */\nexport function note(label: string, ms: number): void {\n if (ENABLED) report(label, ms)\n}\n","import fs from \"node:fs\"\nimport path from \"node:path\"\nimport {\n API_EXTENSIONS,\n NON_ROUTE_DIRS,\n PAGE_EXTENSIONS,\n RESERVED_PAGE_NAMES,\n SRC_DIR,\n} from \"./constants\"\nimport { timed } from \"./profile\"\nimport type { ApiRoute, LayoutRoute, PageRoute, RouteModel, Segment } from \"./types\"\n\n/** Convert an absolute path to a project-relative POSIX path. */\nfunction toPosix(root: string, abs: string): string {\n return path.relative(root, abs).split(path.sep).join(\"/\")\n}\n\n/** Parse one raw path token into a segment descriptor. */\nfunction parseRawSegment(raw: string): Segment {\n const catchall = raw.match(/^\\[\\.\\.\\.(.+)\\]$/)\n if (catchall) return { kind: \"catchall\", name: catchall[1]! }\n const param = raw.match(/^\\[(.+)\\]$/)\n if (param) return { kind: \"param\", name: param[1]! }\n return { kind: \"static\", value: raw }\n}\n\n/** Build a human/display route path from segments. */\nfunction displayPath(segments: Segment[]): string {\n if (segments.length === 0) return \"/\"\n const parts = segments.map((s) => {\n if (s.kind === \"static\") return s.value\n if (s.kind === \"param\") return `:${s.name}`\n return \"*\"\n })\n return \"/\" + parts.join(\"/\")\n}\n\n/** Split a relative POSIX file (without extension) into route segments. */\nfunction fileToSegments(relNoExt: string): Segment[] {\n const raw = relNoExt.split(\"/\")\n // A trailing `index` maps to its directory (drop it).\n if (raw[raw.length - 1] === \"index\") raw.pop()\n return raw.map(parseRawSegment)\n}\n\n/** Strip a known extension from a filename. */\nfunction stripExt(file: string, exts: string[]): string {\n for (const ext of exts) {\n if (file.endsWith(ext)) return file.slice(0, -ext.length)\n }\n return file\n}\n\nfunction hasExt(file: string, exts: string[]): boolean {\n return exts.some((ext) => file.endsWith(ext))\n}\n\n/** Recursively collect files under a directory, respecting ignore rules. */\nfunction walk(dir: string, root: string, out: string[]): void {\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true })\n } catch {\n return\n }\n for (const entry of entries) {\n const abs = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n if (NON_ROUTE_DIRS.has(entry.name)) continue\n if (entry.name.startsWith(\".\")) continue\n walk(abs, root, out)\n } else if (entry.isFile()) {\n out.push(abs)\n }\n }\n}\n\n/**\n * Decide where pages are scanned from: `<root>/src` when that directory exists,\n * the project root otherwise. The two never mix — a project that has `src/`\n * keeps every page inside it (`SRC_DIR_SPLIT` reports the leftovers).\n */\nexport function resolvePageRoot(root: string): { dir: string; abs: string } {\n const abs = path.join(root, SRC_DIR)\n try {\n if (fs.statSync(abs).isDirectory()) return { dir: SRC_DIR, abs }\n } catch {\n /* no src/ — pages live at the project root */\n }\n return { dir: \"\", abs: root }\n}\n\n/**\n * Discover page and layout routes under the page root. `file` stays relative to\n * the *project* root (generated imports resolve from `.vantage/`), while\n * `dir`/`segments`/`routePath` are relative to the *page* root so that `src/`\n * never leaks into a URL — and so `dir` keeps matching `segments` one-to-one,\n * which is how `buildRouteTree` pairs pages with their layouts.\n */\nfunction scanPages(\n root: string,\n pageRoot: string,\n): {\n pages: PageRoute[]\n layouts: LayoutRoute[]\n notFound: string | null\n error: string | null\n} {\n const files: string[] = []\n walk(pageRoot, pageRoot, files)\n\n const pages: PageRoute[] = []\n const layouts: LayoutRoute[] = []\n let notFound: string | null = null\n let error: string | null = null\n\n for (const abs of files) {\n const file = toPosix(root, abs)\n const rel = toPosix(pageRoot, abs)\n const base = path.posix.basename(rel)\n if (!hasExt(base, PAGE_EXTENSIONS)) continue\n\n const nameNoExt = stripExt(base, PAGE_EXTENSIONS)\n const dir = path.posix.dirname(rel)\n const dirPosix = dir === \".\" ? \"\" : dir\n\n // Reserved modules.\n if (nameNoExt === RESERVED_PAGE_NAMES.layout) {\n layouts.push({ file, absFile: abs, dir: dirPosix })\n continue\n }\n if (nameNoExt === RESERVED_PAGE_NAMES.notFound) {\n if (dirPosix === \"\") notFound = file\n continue\n }\n if (nameNoExt === RESERVED_PAGE_NAMES.error) {\n if (dirPosix === \"\") error = file\n continue\n }\n // Any other underscore-prefixed file is a private module, not a route.\n if (nameNoExt.startsWith(\"_\")) continue\n\n const relNoExt = stripExt(rel, PAGE_EXTENSIONS)\n const segments = fileToSegments(relNoExt)\n const routePath = displayPath(segments)\n const dynamic = segments.some((s) => s.kind !== \"static\")\n pages.push({ file, absFile: abs, routePath, segments, dir: dirPosix, dynamic })\n }\n\n // Stable ordering: shallow before deep, static before dynamic, alpha.\n pages.sort((a, b) => routeSortKey(a).localeCompare(routeSortKey(b)))\n layouts.sort((a, b) => a.dir.localeCompare(b.dir))\n return { pages, layouts, notFound, error }\n}\n\n/** Sort key that keeps static routes ahead of dynamic ones at each depth. */\nfunction routeSortKey(p: PageRoute): string {\n return p.segments\n .map((s) => (s.kind === \"static\" ? \"0\" + s.value : s.kind === \"param\" ? \"1\" : \"2\"))\n .join(\"/\")\n}\n\n/** Discover server API routes under `server/api`. */\nfunction scanApis(root: string): ApiRoute[] {\n const apiDir = path.join(root, \"server\", \"api\")\n if (!fs.existsSync(apiDir)) return []\n\n const files: string[] = []\n const walkApi = (dir: string) => {\n let entries: fs.Dirent[]\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true })\n } catch {\n return\n }\n for (const entry of entries) {\n const abs = path.join(dir, entry.name)\n if (entry.isDirectory()) walkApi(abs)\n else if (entry.isFile()) files.push(abs)\n }\n }\n walkApi(apiDir)\n\n const apis: ApiRoute[] = []\n for (const abs of files) {\n const base = path.basename(abs)\n if (!hasExt(base, API_EXTENSIONS)) continue\n const nameNoExt = stripExt(base, API_EXTENSIONS)\n // Type declaration files and private helpers are not routes.\n if (base.endsWith(\".d.ts\")) continue\n if (nameNoExt.startsWith(\"_\")) continue\n\n const relFromServer = toPosix(path.join(root, \"server\"), abs) // e.g. api/customers/[id].ts\n const relNoExt = stripExt(relFromServer, API_EXTENSIONS) // api/customers/[id]\n const withoutApiPrefix = relNoExt.replace(/^api\\/?/, \"\") // customers/[id] or \"\" for api/index\n const segTokens = withoutApiPrefix === \"\" ? [] : withoutApiPrefix.split(\"/\")\n if (segTokens[segTokens.length - 1] === \"index\") segTokens.pop()\n const segments = segTokens.map(parseRawSegment)\n const routePath = \"/api\" + (segments.length ? displayPath(segments) : \"\")\n const params = segments\n .filter((s) => s.kind !== \"static\")\n .map((s) => (s as { name: string }).name)\n\n apis.push({\n file: toPosix(root, abs),\n absFile: abs,\n routePath,\n pattern: routePath,\n segments,\n params,\n })\n }\n\n apis.sort((a, b) => a.routePath.localeCompare(b.routePath))\n return apis\n}\n\n/** Scan a project into a full route model. */\nexport function scanProject(root: string): RouteModel {\n return timed(\"scan\", () => {\n const pageRoot = resolvePageRoot(root)\n const { pages, layouts, notFound, error } = scanPages(root, pageRoot.abs)\n // server/ is always at the project root: it is the other side of the\n // client/server boundary, not part of the client source tree.\n const hasServer = fs.existsSync(path.join(root, \"server\"))\n const apis = hasServer ? scanApis(root) : []\n return { pages, layouts, notFound, error, apis, hasServer, pageDir: pageRoot.dir }\n })\n}\n\n/** Convert a display route path segment list into a TanStack Router path. */\nexport function toRouterPath(segments: Segment[]): string {\n if (segments.length === 0) return \"/\"\n const parts = segments.map((s) => {\n if (s.kind === \"static\") return s.value\n if (s.kind === \"param\") return `$${s.name}`\n return \"$\" // TanStack splat\n })\n return \"/\" + parts.join(\"/\")\n}\n"]}
@@ -0,0 +1,121 @@
1
+ import { PAGE_EXTENSIONS } from './chunk-C4MQRWXS.js';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import pc from 'picocolors';
5
+
6
+ // @squadbase/vantage — generated build. Do not edit.
7
+ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
8
+ "json",
9
+ "help",
10
+ "force",
11
+ "verbose",
12
+ "version",
13
+ "all",
14
+ "list",
15
+ "regex",
16
+ "pages",
17
+ "apis",
18
+ "detail",
19
+ "h",
20
+ "v"
21
+ ]);
22
+ function parseArgs(argv) {
23
+ const positionals = [];
24
+ const flags = {};
25
+ for (let i = 0; i < argv.length; i++) {
26
+ const arg = argv[i];
27
+ if (arg.startsWith("--")) {
28
+ const eq = arg.indexOf("=");
29
+ if (eq !== -1) {
30
+ flags[arg.slice(2, eq)] = arg.slice(eq + 1);
31
+ continue;
32
+ }
33
+ const key = arg.slice(2);
34
+ if (BOOLEAN_FLAGS.has(key)) {
35
+ flags[key] = true;
36
+ continue;
37
+ }
38
+ const next = argv[i + 1];
39
+ if (next !== void 0 && !next.startsWith("-")) {
40
+ flags[key] = next;
41
+ i++;
42
+ } else {
43
+ flags[key] = true;
44
+ }
45
+ } else if (arg.length > 1 && arg.startsWith("-") && !/^-\d/.test(arg)) {
46
+ for (const ch of arg.slice(1)) flags[ch] = true;
47
+ } else {
48
+ positionals.push(arg);
49
+ }
50
+ }
51
+ return { positionals, flags };
52
+ }
53
+ function applyApiBaseUrlFlag(args) {
54
+ const value = args.flags["api-base-url"];
55
+ if (typeof value === "string" && value.length) {
56
+ process.env.PUBLIC_API_BASE_URL = value;
57
+ }
58
+ }
59
+ function parsePort(value, fallback) {
60
+ if (typeof value === "string") {
61
+ const n = Number(value);
62
+ if (Number.isInteger(n) && n > 0 && n < 65536) return n;
63
+ }
64
+ return fallback;
65
+ }
66
+ function resolveRoot(flags) {
67
+ const raw = typeof flags.root === "string" ? flags.root : process.cwd();
68
+ return path.resolve(raw);
69
+ }
70
+ var QUICK_ROOT_MAX_DEPTH = 5;
71
+ function resolveQuickRoot(target) {
72
+ try {
73
+ const abs = path.resolve(target);
74
+ const stat = fs.statSync(abs);
75
+ if (stat.isDirectory()) return abs;
76
+ if (stat.isFile() && PAGE_EXTENSIONS.some((ext) => abs.endsWith(ext))) {
77
+ let dir = path.dirname(abs);
78
+ for (let i = 0; i <= QUICK_ROOT_MAX_DEPTH; i++) {
79
+ if (fs.existsSync(path.join(dir, "package.json"))) return dir;
80
+ const parent = path.dirname(dir);
81
+ if (parent === dir) break;
82
+ dir = parent;
83
+ }
84
+ return path.dirname(abs);
85
+ }
86
+ } catch {
87
+ }
88
+ return null;
89
+ }
90
+ var log = {
91
+ info: (msg) => console.log(msg),
92
+ step: (msg) => console.log(pc.cyan("\u2192 ") + msg),
93
+ ok: (msg) => console.log(pc.green("\u2713 ") + msg),
94
+ warn: (msg) => console.log(pc.yellow("! ") + msg),
95
+ error: (msg) => console.error(pc.red("\u2717 ") + msg),
96
+ brand: (msg) => console.log(pc.bold(pc.magenta("vantage ")) + msg)
97
+ };
98
+ function printDiagnostics(diags) {
99
+ for (const d of diags) {
100
+ const badge = d.severity === "error" ? pc.red(pc.bold(" ERROR ")) : pc.yellow(pc.bold(" WARN "));
101
+ console.log(`${badge} ${pc.dim(d.code)} ${d.message}`);
102
+ if (d.files?.length) console.log(` ${pc.dim("files:")} ${d.files.join(", ")}`);
103
+ if (d.fix) console.log(` ${pc.dim("fix:")} ${d.fix}`);
104
+ }
105
+ }
106
+ function printJson(payload) {
107
+ process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
108
+ }
109
+ function frameworkVersion() {
110
+ try {
111
+ const url = new URL("../package.json", import.meta.url);
112
+ const pkg = JSON.parse(fs.readFileSync(url, "utf8"));
113
+ return pkg.version;
114
+ } catch {
115
+ return "0.0.0";
116
+ }
117
+ }
118
+
119
+ export { applyApiBaseUrlFlag, frameworkVersion, log, parseArgs, parsePort, printDiagnostics, printJson, resolveQuickRoot, resolveRoot };
120
+ //# sourceMappingURL=chunk-OFFJ3OL5.js.map
121
+ //# sourceMappingURL=chunk-OFFJ3OL5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/util.ts"],"names":[],"mappings":"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","file":"chunk-OFFJ3OL5.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 \"h\",\n \"v\",\n])\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 (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/** Parse a `--port` flag value into a valid port, falling back to a default. */\nexport function parsePort(value: string | boolean | undefined, fallback: number): number {\n if (typeof value === \"string\") {\n const n = Number(value)\n if (Number.isInteger(n) && n > 0 && n < 65536) return n\n }\n return fallback\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"]}
@@ -0,0 +1,276 @@
1
+ import { detectApiMethods, stripComments } from './chunk-BAM33YZ6.js';
2
+ import { timed } from './chunk-D4Y5RWJA.js';
3
+ import { PUBLIC_ENV_PREFIX, FORBIDDEN_FILES, PAGE_EXTENSIONS, SRC_DIR, SUSPICIOUS_ROUTE_DIRS, API_METHODS } from './chunk-C4MQRWXS.js';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+
7
+ // @squadbase/vantage — generated build. Do not edit.
8
+ var ALLOWED_ENV_KEYS = /* @__PURE__ */ new Set(["MODE", "DEV", "PROD", "SSR", "BASE_URL", "PUBLIC_"]);
9
+ function hasDefaultExport(src) {
10
+ return /\bexport\s+default\b/.test(src) || /\bexport\s*\{[^}]*\bas\s+default\b[^}]*\}/.test(src) || /\bexport\s*\{[^}]*\bdefault\b[^}]*\}\s*from\b/.test(src);
11
+ }
12
+ function collectClientFiles(root) {
13
+ const out = [];
14
+ const skipDirs = /* @__PURE__ */ new Set(["node_modules", ".vantage", "dist", "server", "public"]);
15
+ const walk = (dir) => {
16
+ let entries;
17
+ try {
18
+ entries = fs.readdirSync(dir, { withFileTypes: true });
19
+ } catch {
20
+ return;
21
+ }
22
+ for (const entry of entries) {
23
+ if (entry.name.startsWith(".")) continue;
24
+ const abs = path.join(dir, entry.name);
25
+ if (entry.isDirectory()) {
26
+ if (skipDirs.has(entry.name)) continue;
27
+ walk(abs);
28
+ } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
29
+ out.push(abs);
30
+ }
31
+ }
32
+ };
33
+ walk(root);
34
+ return out;
35
+ }
36
+ function createCtx(root, model) {
37
+ const sources = /* @__PURE__ */ new Map();
38
+ let clientFiles = null;
39
+ let rootEntries = null;
40
+ return {
41
+ root,
42
+ model,
43
+ source(abs) {
44
+ const hit = sources.get(abs);
45
+ if (hit !== void 0) return hit;
46
+ let src;
47
+ try {
48
+ src = stripComments(fs.readFileSync(abs, "utf8"));
49
+ } catch {
50
+ src = null;
51
+ }
52
+ sources.set(abs, src);
53
+ return src;
54
+ },
55
+ clientFiles() {
56
+ return clientFiles ??= collectClientFiles(root);
57
+ },
58
+ rootEntries() {
59
+ if (rootEntries) return rootEntries;
60
+ try {
61
+ rootEntries = fs.readdirSync(root);
62
+ } catch {
63
+ rootEntries = [];
64
+ }
65
+ return rootEntries;
66
+ }
67
+ };
68
+ }
69
+ function rel(root, abs) {
70
+ return path.relative(root, abs).split(path.sep).join("/");
71
+ }
72
+ function checkForbiddenFiles(ctx) {
73
+ const diags = [];
74
+ for (const name of ctx.rootEntries()) {
75
+ for (const rule of FORBIDDEN_FILES) {
76
+ if (rule.pattern.test(name)) {
77
+ diags.push({
78
+ code: "FORBIDDEN_FILE",
79
+ severity: "error",
80
+ message: `${rule.label} file "${name}" is not allowed in a Vantage project.`,
81
+ files: [name],
82
+ fix: rule.remedy
83
+ });
84
+ }
85
+ }
86
+ }
87
+ return diags;
88
+ }
89
+ function checkSrcLayout(ctx) {
90
+ const { root, model } = ctx;
91
+ if (model.pageDir === "") return [];
92
+ const diags = [];
93
+ const entries = ctx.rootEntries();
94
+ const strayPages = entries.filter((name) => PAGE_EXTENSIONS.some((ext) => name.endsWith(ext)));
95
+ if (strayPages.length > 0) {
96
+ diags.push({
97
+ code: "SRC_DIR_SPLIT",
98
+ severity: "error",
99
+ message: `${strayPages.length} page module(s) sit at the project root, but ${SRC_DIR}/ exists so pages are only scanned from ${SRC_DIR}/.`,
100
+ files: strayPages.sort(),
101
+ fix: `Move them into ${SRC_DIR}/, or delete ${SRC_DIR}/ to keep a root-level layout.`
102
+ });
103
+ }
104
+ if (entries.includes("styles.css")) {
105
+ diags.push({
106
+ code: "SRC_DIR_SPLIT",
107
+ severity: "error",
108
+ message: `styles.css sits at the project root, but ${SRC_DIR}/ exists so the global stylesheet is read from ${SRC_DIR}/styles.css.`,
109
+ files: ["styles.css"],
110
+ fix: `Move it to ${SRC_DIR}/styles.css.`
111
+ });
112
+ }
113
+ if (fs.existsSync(path.join(root, SRC_DIR, "server"))) {
114
+ diags.push({
115
+ code: "SRC_DIR_SPLIT",
116
+ severity: "error",
117
+ message: `${SRC_DIR}/server/ is never scanned: server code lives at the project root, outside the client source tree.`,
118
+ files: [`${SRC_DIR}/server`],
119
+ fix: `Move it to server/ at the project root.`
120
+ });
121
+ }
122
+ return diags;
123
+ }
124
+ function checkSuspiciousRouteDirs(model) {
125
+ const byDir = /* @__PURE__ */ new Map();
126
+ for (const page of model.pages) {
127
+ const top = page.dir.split("/")[0] ?? "";
128
+ if (top === "" || !SUSPICIOUS_ROUTE_DIRS.has(top)) continue;
129
+ const hit = byDir.get(top) ?? { files: [], routes: [] };
130
+ hit.files.push(page.file);
131
+ hit.routes.push(page.routePath);
132
+ byDir.set(top, hit);
133
+ }
134
+ const diags = [];
135
+ for (const [dir, hit] of byDir) {
136
+ diags.push({
137
+ code: "SUSPICIOUS_ROUTE_DIR",
138
+ severity: "warning",
139
+ message: `${dir}/ is part of the URL: ${hit.routes.slice(0, 3).join(", ")}${hit.routes.length > 3 ? ", \u2026" : ""}`,
140
+ files: hit.files,
141
+ fix: `Directory names become route segments. Non-page modules belong in components/, hooks/ or lib/ (never scanned); if ${dir}/ really is meant to be a URL segment, ignore this warning.`
142
+ });
143
+ }
144
+ return diags;
145
+ }
146
+ function checkRouteConflicts(model) {
147
+ const byPath = /* @__PURE__ */ new Map();
148
+ for (const page of model.pages) {
149
+ const list = byPath.get(page.routePath) ?? [];
150
+ list.push(page.file);
151
+ byPath.set(page.routePath, list);
152
+ }
153
+ const diags = [];
154
+ for (const [routePath, files] of byPath) {
155
+ if (files.length > 1) {
156
+ diags.push({
157
+ code: "ROUTE_CONFLICT",
158
+ severity: "error",
159
+ message: `${files.length} files resolve to ${routePath}`,
160
+ files,
161
+ fix: "Rename or remove one route file so each route is unique."
162
+ });
163
+ }
164
+ }
165
+ return diags;
166
+ }
167
+ function checkPageExports(ctx) {
168
+ const { root, model } = ctx;
169
+ const diags = [];
170
+ const pageFiles = [
171
+ ...model.pages.map((p) => p.absFile),
172
+ ...model.layouts.map((l) => l.absFile),
173
+ model.notFound && path.join(root, model.notFound),
174
+ model.error && path.join(root, model.error)
175
+ ].filter((x) => Boolean(x));
176
+ for (const abs of pageFiles) {
177
+ const src = ctx.source(abs);
178
+ if (src === null) continue;
179
+ if (!hasDefaultExport(src)) {
180
+ diags.push({
181
+ code: "MISSING_DEFAULT_EXPORT",
182
+ severity: "error",
183
+ message: `Page module has no default export.`,
184
+ files: [rel(root, abs)],
185
+ fix: "A page/layout must `export default` a React component."
186
+ });
187
+ }
188
+ }
189
+ return diags;
190
+ }
191
+ function checkApiExports(ctx) {
192
+ const diags = [];
193
+ for (const api of ctx.model.apis) {
194
+ const src = ctx.source(api.absFile);
195
+ if (src === null) continue;
196
+ const exported = detectApiMethods(src);
197
+ if (exported.length === 0) {
198
+ const lower = ["get", "post", "put", "patch", "delete", "options"].filter(
199
+ (m) => new RegExp(`export\\s+(async\\s+)?function\\s+${m}\\b`).test(src)
200
+ );
201
+ diags.push({
202
+ code: "INVALID_API_EXPORT",
203
+ severity: "error",
204
+ message: `API module exports no HTTP method handler${lower.length ? ` (found lowercase "${lower.join(", ")}")` : ""}.`,
205
+ files: [api.file],
206
+ fix: `Export one of: ${API_METHODS.join(", ")} (uppercase).`
207
+ });
208
+ }
209
+ }
210
+ return diags;
211
+ }
212
+ function checkClientSources(ctx) {
213
+ const { root } = ctx;
214
+ const boundary = [];
215
+ const env = [];
216
+ const importRe = /(?:import|export)[^'"]*?from\s+["']([^"']+)["']|import\s*\(\s*["']([^"']+)["']\s*\)/g;
217
+ const envRe = /import\.meta\.env\.([A-Za-z_][A-Za-z0-9_]*)/g;
218
+ for (const abs of ctx.clientFiles()) {
219
+ const src = ctx.source(abs);
220
+ if (src === null) continue;
221
+ let m;
222
+ importRe.lastIndex = 0;
223
+ while (m = importRe.exec(src)) {
224
+ const spec = m[1] ?? m[2];
225
+ if (!spec) continue;
226
+ const isServer = spec === "server" || spec.startsWith("server/") || /(^|\/)\.\.?\/server(\/|$)/.test(spec) || /\/server\//.test(spec);
227
+ if (isServer) {
228
+ boundary.push({
229
+ code: "CLIENT_IMPORTS_SERVER",
230
+ severity: "error",
231
+ message: `Client module imports from server/ ("${spec}").`,
232
+ files: [rel(root, abs)],
233
+ fix: "server/ code must never be imported by the client. Move shared code to lib/."
234
+ });
235
+ }
236
+ }
237
+ envRe.lastIndex = 0;
238
+ const bad = /* @__PURE__ */ new Set();
239
+ while (m = envRe.exec(src)) {
240
+ const key = m[1];
241
+ if (ALLOWED_ENV_KEYS.has(key)) continue;
242
+ if (key.startsWith(PUBLIC_ENV_PREFIX)) continue;
243
+ bad.add(key);
244
+ }
245
+ for (const key of bad) {
246
+ env.push({
247
+ code: "PUBLIC_ENV_MISUSE",
248
+ severity: "warning",
249
+ message: `Client reads non-public env "import.meta.env.${key}".`,
250
+ files: [rel(root, abs)],
251
+ fix: `Only ${PUBLIC_ENV_PREFIX}* vars reach the client. Read secrets via ApiContext.env in server/.`
252
+ });
253
+ }
254
+ }
255
+ return { boundary, env };
256
+ }
257
+ function runDiagnostics(root, model) {
258
+ return timed("diagnostics", () => {
259
+ const ctx = createCtx(root, model);
260
+ const client = checkClientSources(ctx);
261
+ return [
262
+ ...checkForbiddenFiles(ctx),
263
+ ...checkSrcLayout(ctx),
264
+ ...checkSuspiciousRouteDirs(model),
265
+ ...checkRouteConflicts(model),
266
+ ...checkPageExports(ctx),
267
+ ...checkApiExports(ctx),
268
+ ...client.boundary,
269
+ ...client.env
270
+ ];
271
+ });
272
+ }
273
+
274
+ export { runDiagnostics };
275
+ //# sourceMappingURL=chunk-PSEYFYAP.js.map
276
+ //# sourceMappingURL=chunk-PSEYFYAP.js.map