@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
@@ -1,762 +0,0 @@
1
- import { corsMiddleware, matchApiRoute, dispatchApi, sortApiRoutes } from './chunk-46QI6GFC.js';
2
- import fs2 from 'fs';
3
- import path3 from 'path';
4
- import react from '@vitejs/plugin-react';
5
- import tailwindcss from '@tailwindcss/vite';
6
- import pc from 'picocolors';
7
- import { createRequire } from 'module';
8
- import { Hono } from 'hono';
9
- import { getRequestListener } from '@hono/node-server';
10
-
11
- // @squadbase/vantage — generated build. Do not edit.
12
-
13
- // src/core/constants.ts
14
- var VANTAGE_DIR = ".vantage";
15
- var NON_ROUTE_DIRS = /* @__PURE__ */ new Set([
16
- "components",
17
- "hooks",
18
- "lib",
19
- "server",
20
- "public",
21
- "node_modules",
22
- VANTAGE_DIR,
23
- "dist"
24
- ]);
25
- var PAGE_EXTENSIONS = [".tsx", ".jsx"];
26
- var API_EXTENSIONS = [".ts", ".tsx", ".js"];
27
- var RESERVED_PAGE_NAMES = {
28
- layout: "_layout",
29
- notFound: "_404",
30
- error: "_error"
31
- };
32
- var API_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"];
33
- var FORBIDDEN_FILES = [
34
- {
35
- pattern: /^vite\.config\.(ts|js|mjs|cjs|mts|cts)$/,
36
- label: "Vite config",
37
- remedy: "Vantage owns the Vite configuration. Remove this file."
38
- },
39
- {
40
- pattern: /^tailwind\.config\.(ts|js|mjs|cjs)$/,
41
- label: "Tailwind config",
42
- remedy: "Customize theme tokens in styles.css instead."
43
- },
44
- {
45
- pattern: /^postcss\.config\.(ts|js|mjs|cjs)$/,
46
- label: "PostCSS config",
47
- remedy: "Vantage manages PostCSS/Tailwind. Remove this file."
48
- },
49
- {
50
- pattern: /^components\.json$/,
51
- label: "shadcn components.json",
52
- remedy: "Use `vantage add ui <name>` to copy UI source instead."
53
- },
54
- {
55
- pattern: /^vantage\.config\.(ts|js|mjs|cjs)$/,
56
- label: "Vantage config",
57
- remedy: "v1 has no vantage.config. Configuration lives in file conventions."
58
- }
59
- ];
60
- var PUBLIC_ENV_PREFIX = "PUBLIC_";
61
- var MANIFEST_SCHEMA_VERSION = 1;
62
- function segToRouter(s) {
63
- if (s.kind === "static") return s.value;
64
- if (s.kind === "param") return `$${s.name}`;
65
- return "$";
66
- }
67
- function dirToRouterPath(relDir) {
68
- return relDir.split("/").filter(Boolean).map((token) => {
69
- const catchall = token.match(/^\[\.\.\.(.+)\]$/);
70
- if (catchall) return "$";
71
- const param = token.match(/^\[(.+)\]$/);
72
- if (param) return `$${param[1]}`;
73
- return token;
74
- }).join("/");
75
- }
76
- function isAncestorDir(ancestor, dir) {
77
- if (ancestor === "") return true;
78
- return dir === ancestor || dir.startsWith(ancestor + "/");
79
- }
80
- function isStrictAncestorDir(ancestor, dir) {
81
- return dir.startsWith(ancestor + "/");
82
- }
83
- function depth(dir) {
84
- return dir === "" ? 0 : dir.split("/").length;
85
- }
86
- function extractDefinePageArgs(src) {
87
- const call = src.match(/\bdefinePage\s*\(/);
88
- if (!call) return null;
89
- let i = (call.index ?? 0) + call[0].length;
90
- while (i < src.length && /\s/.test(src[i])) i++;
91
- if (src[i] !== "{") return null;
92
- const start = i;
93
- let depth2 = 0;
94
- for (; i < src.length; i++) {
95
- const c = src[i];
96
- if (c === "{") depth2++;
97
- else if (c === "}" && --depth2 === 0) return src.slice(start + 1, i);
98
- }
99
- return null;
100
- }
101
- function matchStringField(scope, key) {
102
- const re = new RegExp(
103
- `\\b${key}\\s*:\\s*(?:"((?:\\\\.|[^"\\\\])*)"|'((?:\\\\.|[^'\\\\])*)'|\`((?:\\\\.|[^\`\\\\])*)\`)`
104
- );
105
- const m = scope.match(re);
106
- if (!m) return void 0;
107
- return (m[1] ?? m[2] ?? m[3] ?? "").replace(/\\(.)/g, "$1");
108
- }
109
- function extractPageMeta(absFile) {
110
- let src;
111
- try {
112
- src = fs2.readFileSync(absFile, "utf8");
113
- } catch {
114
- return {};
115
- }
116
- const args = extractDefinePageArgs(src);
117
- if (args == null) return {};
118
- const meta = {};
119
- const title = matchStringField(args, "title");
120
- const description = matchStringField(args, "description");
121
- const navLabel = matchStringField(args, "navLabel");
122
- if (title) meta.title = title;
123
- if (description) meta.description = description;
124
- if (navLabel) meta.navLabel = navLabel;
125
- return meta;
126
- }
127
- function buildRouteTree(model) {
128
- const rootLayoutRoute = model.layouts.find((l) => l.dir === "") ?? null;
129
- const nestedLayouts = model.layouts.filter((l) => l.dir !== "").sort((a, b) => depth(a.dir) - depth(b.dir));
130
- const built = [];
131
- const dirToId = /* @__PURE__ */ new Map();
132
- nestedLayouts.forEach((layout, i) => {
133
- const id = `L${i}`;
134
- let parent = null;
135
- for (const cand of built) {
136
- if (isStrictAncestorDir(cand.dir, layout.dir)) {
137
- if (!parent || depth(cand.dir) > depth(parent.dir)) parent = { id: cand.id, dir: cand.dir };
138
- }
139
- }
140
- const parentDir = parent ? parent.dir : "";
141
- const relDir = parentDir === "" ? layout.dir : layout.dir.slice(parentDir.length + 1);
142
- built.push({
143
- id,
144
- dir: layout.dir,
145
- parentId: parent ? parent.id : null,
146
- path: dirToRouterPath(relDir),
147
- file: layout.file
148
- });
149
- dirToId.set(layout.dir, id);
150
- });
151
- const pages = model.pages.map((page) => {
152
- let owner = null;
153
- for (const l of built) {
154
- if (isAncestorDir(l.dir, page.dir)) {
155
- if (!owner || depth(l.dir) > depth(owner.dir)) owner = l;
156
- }
157
- }
158
- const ownerSegCount = owner ? depth(owner.dir) : 0;
159
- const remaining = page.segments.slice(ownerSegCount);
160
- const index = remaining.length === 0;
161
- const path4 = index ? "/" : remaining.map(segToRouter).join("/");
162
- const meta = extractPageMeta(page.absFile);
163
- return {
164
- file: page.file,
165
- routePath: page.routePath,
166
- parentLayoutId: owner ? owner.id : null,
167
- path: path4,
168
- index,
169
- ...meta.title ? { title: meta.title } : {},
170
- ...meta.description ? { description: meta.description } : {},
171
- ...meta.navLabel ? { navLabel: meta.navLabel } : {}
172
- };
173
- });
174
- return {
175
- rootLayout: rootLayoutRoute ? rootLayoutRoute.file : null,
176
- notFound: model.notFound,
177
- error: model.error,
178
- layouts: built,
179
- pages
180
- };
181
- }
182
-
183
- // src/core/generate.ts
184
- var PKG = "@squadbase/vantage";
185
- function resolveVantageDir(root) {
186
- try {
187
- const require2 = createRequire(path3.join(root, "noop.js"));
188
- const pkgJson = require2.resolve(`${PKG}/package.json`);
189
- return path3.dirname(pkgJson);
190
- } catch {
191
- return null;
192
- }
193
- }
194
- function importFromVantage(relFile) {
195
- return `../${relFile}`;
196
- }
197
- function ensureDir(dir) {
198
- fs2.mkdirSync(dir, { recursive: true });
199
- }
200
- function writeIfChanged(file, content) {
201
- try {
202
- if (fs2.existsSync(file) && fs2.readFileSync(file, "utf8") === content) return;
203
- } catch {
204
- }
205
- fs2.writeFileSync(file, content);
206
- }
207
- function serializeSegments(segments) {
208
- return "[" + segments.map((s) => {
209
- if (s.kind === "static") return `{ kind: "static", value: ${JSON.stringify(s.value)} }`;
210
- if (s.kind === "param") return `{ kind: "param", name: ${JSON.stringify(s.name)} }`;
211
- return `{ kind: "catchall", name: ${JSON.stringify(s.name)} }`;
212
- }).join(", ") + "]";
213
- }
214
- var BANNER = "// AUTO-GENERATED by @squadbase/vantage. Do not edit \u2014 regenerated on every run.\n";
215
- function renderRouteTree(model) {
216
- const tree = buildRouteTree(model);
217
- const lines = [];
218
- lines.push(BANNER);
219
- lines.push(`import type { ClientManifest } from "${PKG}/client"`);
220
- lines.push("");
221
- lines.push("export const manifest: ClientManifest = {");
222
- lines.push(
223
- ` rootLayout: ${tree.rootLayout ? `() => import(${JSON.stringify(importFromVantage(tree.rootLayout))})` : "null"},`
224
- );
225
- lines.push(
226
- ` notFound: ${tree.notFound ? `() => import(${JSON.stringify(importFromVantage(tree.notFound))})` : "null"},`
227
- );
228
- lines.push(
229
- ` error: ${tree.error ? `() => import(${JSON.stringify(importFromVantage(tree.error))})` : "null"},`
230
- );
231
- lines.push(" layouts: [");
232
- for (const l of tree.layouts) {
233
- lines.push(
234
- ` { id: ${JSON.stringify(l.id)}, parentId: ${JSON.stringify(l.parentId)}, path: ${JSON.stringify(
235
- l.path
236
- )}, load: () => import(${JSON.stringify(importFromVantage(l.file))}) },`
237
- );
238
- }
239
- lines.push(" ],");
240
- lines.push(" pages: [");
241
- for (const p of tree.pages) {
242
- const extra = [];
243
- if (p.title) extra.push(`title: ${JSON.stringify(p.title)}`);
244
- if (p.description) extra.push(`description: ${JSON.stringify(p.description)}`);
245
- if (p.navLabel) extra.push(`navLabel: ${JSON.stringify(p.navLabel)}`);
246
- lines.push(
247
- ` { parentLayoutId: ${JSON.stringify(p.parentLayoutId)}, path: ${JSON.stringify(
248
- p.path
249
- )}, index: ${p.index}, routePath: ${JSON.stringify(p.routePath)}${extra.length ? ", " + extra.join(", ") : ""}, load: () => import(${JSON.stringify(importFromVantage(p.file))}) },`
250
- );
251
- }
252
- lines.push(" ],");
253
- lines.push("}");
254
- lines.push("");
255
- return lines.join("\n");
256
- }
257
- function renderServerRoutes(model) {
258
- const lines = [];
259
- lines.push(BANNER);
260
- lines.push(`import type { ProdApiRoute } from "${PKG}/server/node"`);
261
- model.apis.forEach((a, i) => {
262
- lines.push(`import * as m${i} from ${JSON.stringify(importFromVantage(a.file))}`);
263
- });
264
- lines.push("");
265
- lines.push("export const apiRoutes: ProdApiRoute[] = [");
266
- model.apis.forEach((a, i) => {
267
- lines.push(
268
- ` { routePath: ${JSON.stringify(a.routePath)}, segments: ${serializeSegments(
269
- a.segments
270
- )}, module: m${i} },`
271
- );
272
- });
273
- lines.push("]");
274
- lines.push("");
275
- return lines.join("\n");
276
- }
277
- function renderIndexHtml() {
278
- return `<!doctype html>
279
- <html lang="en">
280
- <head>
281
- <meta charset="UTF-8" />
282
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
283
- <title>Vantage</title>
284
- </head>
285
- <body>
286
- <div id="root"></div>
287
- <script type="module" src="/.vantage/client-entry.tsx"></script>
288
- </body>
289
- </html>
290
- `;
291
- }
292
- function renderClientEntry() {
293
- return `${BANNER}import { bootstrap } from "${PKG}/client"
294
- import { manifest } from "./route-tree.gen"
295
- import "./runtime.css"
296
-
297
- bootstrap(manifest)
298
- `;
299
- }
300
- function renderRuntimeCss(root) {
301
- const parts = [];
302
- parts.push('@import "tailwindcss";');
303
- parts.push(`@import "${PKG}/theme.css";`);
304
- if (fs2.existsSync(path3.join(root, "styles.css"))) {
305
- parts.push('@import "../styles.css";');
306
- }
307
- const vantageDir = resolveVantageDir(root);
308
- if (vantageDir) {
309
- parts.push(`@source ${JSON.stringify(path3.join(vantageDir, "dist"))};`);
310
- }
311
- return parts.join("\n") + "\n";
312
- }
313
- function renderEnvTypes() {
314
- return `${BANNER}
315
- interface ImportMetaEnv {
316
- readonly MODE: string
317
- readonly DEV: boolean
318
- readonly PROD: boolean
319
- readonly SSR: boolean
320
- readonly BASE_URL: string
321
- readonly [key: \`PUBLIC_\${string}\`]: string | undefined
322
- }
323
-
324
- interface ImportMeta {
325
- readonly env: ImportMetaEnv
326
- }
327
-
328
- declare module "*.css" {
329
- const content: string
330
- export default content
331
- }
332
- `;
333
- }
334
- function renderInternalManifest(model) {
335
- return JSON.stringify(
336
- {
337
- schemaVersion: MANIFEST_SCHEMA_VERSION,
338
- pages: model.pages.map((p) => ({ file: p.file, route: p.routePath })),
339
- apis: model.apis.map((a) => ({ file: a.file, route: a.routePath })),
340
- hasServer: model.hasServer
341
- },
342
- null,
343
- 2
344
- ) + "\n";
345
- }
346
- function generateArtifacts({ root, model }) {
347
- const vantageDir = path3.join(root, VANTAGE_DIR);
348
- ensureDir(vantageDir);
349
- ensureDir(path3.join(vantageDir, "types"));
350
- writeIfChanged(path3.join(vantageDir, ".gitignore"), "*\n");
351
- writeIfChanged(path3.join(vantageDir, "index.html"), renderIndexHtml());
352
- writeIfChanged(path3.join(vantageDir, "client-entry.tsx"), renderClientEntry());
353
- writeIfChanged(path3.join(vantageDir, "route-tree.gen.ts"), renderRouteTree(model));
354
- writeIfChanged(path3.join(vantageDir, "runtime.css"), renderRuntimeCss(root));
355
- writeIfChanged(path3.join(vantageDir, "types", "env.d.ts"), renderEnvTypes());
356
- writeIfChanged(path3.join(vantageDir, "manifest.json"), renderInternalManifest(model));
357
- if (model.hasServer) {
358
- writeIfChanged(path3.join(vantageDir, "server-routes.gen.ts"), renderServerRoutes(model));
359
- }
360
- return vantageDir;
361
- }
362
- function toPosix(root, abs) {
363
- return path3.relative(root, abs).split(path3.sep).join("/");
364
- }
365
- function parseRawSegment(raw) {
366
- const catchall = raw.match(/^\[\.\.\.(.+)\]$/);
367
- if (catchall) return { kind: "catchall", name: catchall[1] };
368
- const param = raw.match(/^\[(.+)\]$/);
369
- if (param) return { kind: "param", name: param[1] };
370
- return { kind: "static", value: raw };
371
- }
372
- function displayPath(segments) {
373
- if (segments.length === 0) return "/";
374
- const parts = segments.map((s) => {
375
- if (s.kind === "static") return s.value;
376
- if (s.kind === "param") return `:${s.name}`;
377
- return "*";
378
- });
379
- return "/" + parts.join("/");
380
- }
381
- function fileToSegments(relNoExt) {
382
- const raw = relNoExt.split("/");
383
- if (raw[raw.length - 1] === "index") raw.pop();
384
- return raw.map(parseRawSegment);
385
- }
386
- function stripExt(file, exts) {
387
- for (const ext of exts) {
388
- if (file.endsWith(ext)) return file.slice(0, -ext.length);
389
- }
390
- return file;
391
- }
392
- function hasExt(file, exts) {
393
- return exts.some((ext) => file.endsWith(ext));
394
- }
395
- function walk(dir, root, out) {
396
- let entries;
397
- try {
398
- entries = fs2.readdirSync(dir, { withFileTypes: true });
399
- } catch {
400
- return;
401
- }
402
- for (const entry of entries) {
403
- const abs = path3.join(dir, entry.name);
404
- if (entry.isDirectory()) {
405
- if (NON_ROUTE_DIRS.has(entry.name)) continue;
406
- if (entry.name.startsWith(".")) continue;
407
- walk(abs, root, out);
408
- } else if (entry.isFile()) {
409
- out.push(abs);
410
- }
411
- }
412
- }
413
- function scanPages(root) {
414
- const files = [];
415
- walk(root, root, files);
416
- const pages = [];
417
- const layouts = [];
418
- let notFound = null;
419
- let error = null;
420
- for (const abs of files) {
421
- const rel = toPosix(root, abs);
422
- const base = path3.posix.basename(rel);
423
- if (!hasExt(base, PAGE_EXTENSIONS)) continue;
424
- const nameNoExt = stripExt(base, PAGE_EXTENSIONS);
425
- const dir = path3.posix.dirname(rel);
426
- const dirPosix = dir === "." ? "" : dir;
427
- if (nameNoExt === RESERVED_PAGE_NAMES.layout) {
428
- layouts.push({ file: rel, absFile: abs, dir: dirPosix });
429
- continue;
430
- }
431
- if (nameNoExt === RESERVED_PAGE_NAMES.notFound) {
432
- if (dirPosix === "") notFound = rel;
433
- continue;
434
- }
435
- if (nameNoExt === RESERVED_PAGE_NAMES.error) {
436
- if (dirPosix === "") error = rel;
437
- continue;
438
- }
439
- if (nameNoExt.startsWith("_")) continue;
440
- const relNoExt = stripExt(rel, PAGE_EXTENSIONS);
441
- const segments = fileToSegments(relNoExt);
442
- const routePath = displayPath(segments);
443
- const dynamic = segments.some((s) => s.kind !== "static");
444
- pages.push({ file: rel, absFile: abs, routePath, segments, dir: dirPosix, dynamic });
445
- }
446
- pages.sort((a, b) => routeSortKey(a).localeCompare(routeSortKey(b)));
447
- layouts.sort((a, b) => a.dir.localeCompare(b.dir));
448
- return { pages, layouts, notFound, error };
449
- }
450
- function routeSortKey(p) {
451
- return p.segments.map((s) => s.kind === "static" ? "0" + s.value : s.kind === "param" ? "1" : "2").join("/");
452
- }
453
- function scanApis(root) {
454
- const apiDir = path3.join(root, "server", "api");
455
- if (!fs2.existsSync(apiDir)) return [];
456
- const files = [];
457
- const walkApi = (dir) => {
458
- let entries;
459
- try {
460
- entries = fs2.readdirSync(dir, { withFileTypes: true });
461
- } catch {
462
- return;
463
- }
464
- for (const entry of entries) {
465
- const abs = path3.join(dir, entry.name);
466
- if (entry.isDirectory()) walkApi(abs);
467
- else if (entry.isFile()) files.push(abs);
468
- }
469
- };
470
- walkApi(apiDir);
471
- const apis = [];
472
- for (const abs of files) {
473
- const base = path3.basename(abs);
474
- if (!hasExt(base, API_EXTENSIONS)) continue;
475
- const nameNoExt = stripExt(base, API_EXTENSIONS);
476
- if (base.endsWith(".d.ts")) continue;
477
- if (nameNoExt.startsWith("_")) continue;
478
- const relFromServer = toPosix(path3.join(root, "server"), abs);
479
- const relNoExt = stripExt(relFromServer, API_EXTENSIONS);
480
- const withoutApiPrefix = relNoExt.replace(/^api\/?/, "");
481
- const segTokens = withoutApiPrefix === "" ? [] : withoutApiPrefix.split("/");
482
- if (segTokens[segTokens.length - 1] === "index") segTokens.pop();
483
- const segments = segTokens.map(parseRawSegment);
484
- const routePath = "/api" + (segments.length ? displayPath(segments) : "");
485
- const params = segments.filter((s) => s.kind !== "static").map((s) => s.name);
486
- apis.push({
487
- file: toPosix(root, abs),
488
- absFile: abs,
489
- routePath,
490
- pattern: routePath,
491
- segments,
492
- params
493
- });
494
- }
495
- apis.sort((a, b) => a.routePath.localeCompare(b.routePath));
496
- return apis;
497
- }
498
- function scanProject(root) {
499
- const { pages, layouts, notFound, error } = scanPages(root);
500
- const hasServer = fs2.existsSync(path3.join(root, "server"));
501
- const apis = hasServer ? scanApis(root) : [];
502
- return { pages, layouts, notFound, error, apis, hasServer };
503
- }
504
- function createApiDevMiddleware(opts) {
505
- const { server, getModel, getEnv } = opts;
506
- const buildRoutes = () => sortApiRoutes(
507
- getModel().apis.map((a) => ({
508
- routePath: a.routePath,
509
- segments: a.segments,
510
- hasCatchall: a.segments.some((s) => s.kind === "catchall"),
511
- staticCount: a.segments.filter((s) => s.kind === "static").length,
512
- load: () => server.ssrLoadModule(a.absFile)
513
- }))
514
- );
515
- const app = new Hono();
516
- const cors = corsMiddleware(getEnv());
517
- if (cors) {
518
- app.use("/api", cors);
519
- app.use("/api/*", cors);
520
- }
521
- const handle = async (c) => {
522
- const url = new URL(c.req.url);
523
- const match = matchApiRoute(buildRoutes(), url.pathname);
524
- if (!match) {
525
- return Response.json({ error: "Not Found", path: url.pathname }, { status: 404 });
526
- }
527
- let mod;
528
- try {
529
- mod = await match.route.load();
530
- } catch (err) {
531
- if (err instanceof Error) server.ssrFixStacktrace(err);
532
- throw err;
533
- }
534
- return dispatchApi(mod, c.req.raw, match.params, getEnv());
535
- };
536
- app.all("/api", handle);
537
- app.all("/api/*", handle);
538
- const listener = getRequestListener(app.fetch);
539
- return (req, res, next) => {
540
- const url = req.url ?? "/";
541
- const path4 = url.split("?")[0];
542
- if (path4 === "/api" || path4.startsWith("/api/")) {
543
- Promise.resolve(listener(req, res)).catch((err) => {
544
- console.error("[vantage] API middleware failure:", err);
545
- if (!res.headersSent) {
546
- res.statusCode = 500;
547
- res.setHeader("content-type", "application/json");
548
- res.end(JSON.stringify({ error: "Internal Server Error" }));
549
- }
550
- });
551
- } else {
552
- next();
553
- }
554
- };
555
- }
556
-
557
- // src/vite/index.ts
558
- var DEDUPE = ["react", "react-dom", "@tanstack/react-router", "@tanstack/react-query"];
559
- function isPageFile(root, file) {
560
- const rel = path3.relative(root, file);
561
- if (rel.startsWith("..")) return false;
562
- const segments = rel.split(path3.sep);
563
- if (segments.some((s) => NON_ROUTE_DIRS.has(s) || s.startsWith(".") && s !== "..")) return false;
564
- return PAGE_EXTENSIONS.some((ext) => file.endsWith(ext));
565
- }
566
- function isApiFile(root, file) {
567
- const rel = path3.relative(root, file);
568
- return rel.startsWith(`server${path3.sep}api${path3.sep}`) && /\.(ts|tsx|js)$/.test(file) && !file.endsWith(".d.ts");
569
- }
570
- function isServerFile(root, file) {
571
- const rel = path3.relative(root, file);
572
- return rel === "server" || rel.startsWith(`server${path3.sep}`);
573
- }
574
- function vantagePlugin(options) {
575
- const root = path3.resolve(options.root);
576
- const vantageDir = path3.join(root, VANTAGE_DIR);
577
- let model = scanProject(root);
578
- const regenerate = () => {
579
- model = scanProject(root);
580
- generateArtifacts({ root, model });
581
- };
582
- const core = {
583
- name: "vantage:core",
584
- enforce: "pre",
585
- config() {
586
- return {
587
- root,
588
- appType: "custom",
589
- envPrefix: ["PUBLIC_"],
590
- resolve: { dedupe: DEDUPE },
591
- optimizeDeps: {
592
- // Only list deps resolvable from the project root here; the managed
593
- // @tanstack/* packages are discovered through the module graph.
594
- include: ["react", "react-dom", "react-dom/client", "react/jsx-runtime"]
595
- },
596
- build: {
597
- outDir: path3.join(root, "dist", "client"),
598
- emptyOutDir: true,
599
- rollupOptions: {
600
- input: path3.join(vantageDir, "index.html")
601
- }
602
- }
603
- };
604
- },
605
- configResolved() {
606
- regenerate();
607
- },
608
- buildStart() {
609
- regenerate();
610
- },
611
- // --- Server-import boundary: client code may never import server/. ---
612
- resolveId(source, importer, opts) {
613
- if (opts?.ssr) return null;
614
- if (!importer) return null;
615
- const imp = importer.split("?")[0];
616
- if (!imp.startsWith(root)) return null;
617
- const relImp = path3.relative(root, imp);
618
- if (relImp.startsWith(`server${path3.sep}`) || relImp.startsWith(VANTAGE_DIR) || relImp.includes("node_modules")) {
619
- return null;
620
- }
621
- let target = null;
622
- if (source.startsWith(".")) target = path3.resolve(path3.dirname(imp), source);
623
- else if (source.startsWith("/")) target = path3.join(root, source.slice(1));
624
- else if (source === "server" || source.startsWith("server/")) target = path3.join(root, source);
625
- if (!target) return null;
626
- const relTarget = path3.relative(root, target);
627
- if (relTarget === "server" || relTarget.startsWith(`server${path3.sep}`)) {
628
- this.error(
629
- `Client module "${relImp}" imports from server/ ("${source}"). server/ code must never reach the client bundle \u2014 move shared code to lib/.`
630
- );
631
- }
632
- return null;
633
- },
634
- // --- Keep React Fast Refresh working on page modules. ---
635
- // `export const page = definePage(...)` is a non-component export, which
636
- // makes @vitejs/plugin-react bail out of Fast Refresh (full reload). The
637
- // runtime never reads this export (title/description are extracted at
638
- // generate time), so we drop the `export` keyword — turning it into a local
639
- // const — before the React plugin analyzes the module. Offsets are
640
- // preserved (6 spaces for "export") so sourcemaps stay accurate.
641
- transform(code, id) {
642
- const clean = id.split("?")[0];
643
- if (!isPageFile(root, clean)) return null;
644
- if (!/export\s+const\s+page\b/.test(code)) return null;
645
- const out = code.replace(
646
- /^([ \t]*)export(\s+const\s+page\b)/gm,
647
- (_m, indent, rest) => indent + " " + rest
648
- );
649
- if (out === code) return null;
650
- return { code: out, map: null };
651
- },
652
- // --- Dev console + runtime errors forwarded to the terminal. ---
653
- transformIndexHtml: {
654
- order: "pre",
655
- handler(_html, ctx) {
656
- if (!ctx.server) return [];
657
- return [
658
- {
659
- tag: "script",
660
- attrs: { type: "module" },
661
- injectTo: "head-prepend",
662
- children: CONSOLE_BRIDGE
663
- }
664
- ];
665
- }
666
- },
667
- configureServer(server) {
668
- const getEnv = () => options.env ?? process.env;
669
- const apiMw = createApiDevMiddleware({ server, getModel: () => model, getEnv });
670
- server.middlewares.use((req, res, next) => {
671
- const url = (req.url ?? "/").split("?")[0];
672
- if (url === "/__vantage_console" && req.method === "POST") {
673
- return handleConsoleBeacon(req, res);
674
- }
675
- if (model.hasServer) return apiMw(req, res, next);
676
- return next();
677
- });
678
- const isStylesFile = (file) => path3.relative(root, file) === "styles.css";
679
- const onStructural = (file) => {
680
- if (isPageFile(root, file) || isApiFile(root, file) || isStylesFile(file)) {
681
- regenerate();
682
- server.ws.send({ type: "full-reload" });
683
- }
684
- };
685
- server.watcher.on("add", onStructural);
686
- server.watcher.on("unlink", onStructural);
687
- server.watcher.on("addDir", () => {
688
- regenerate();
689
- });
690
- server.watcher.on("unlinkDir", () => {
691
- regenerate();
692
- server.ws.send({ type: "full-reload" });
693
- });
694
- server.watcher.on("change", (file) => {
695
- if (isServerFile(root, file)) {
696
- const mods = server.moduleGraph.getModulesByFile(file);
697
- if (mods) for (const m of mods) server.moduleGraph.invalidateModule(m);
698
- }
699
- });
700
- return () => {
701
- server.middlewares.use(async (req, res, next) => {
702
- if (req.method !== "GET" && req.method !== "HEAD") return next();
703
- try {
704
- const raw = fs2.readFileSync(path3.join(vantageDir, "index.html"), "utf8");
705
- const shellUrl = `/${VANTAGE_DIR}/index.html`;
706
- const html = await server.transformIndexHtml(shellUrl, raw, req.originalUrl);
707
- res.statusCode = 200;
708
- res.setHeader("Content-Type", "text/html");
709
- res.end(html);
710
- } catch (err) {
711
- next(err);
712
- }
713
- });
714
- };
715
- }
716
- };
717
- return [core, react(), tailwindcss()];
718
- }
719
- var CONSOLE_BRIDGE = `
720
- (() => {
721
- const send = (level, message) => {
722
- try {
723
- const body = JSON.stringify({ level, message });
724
- if (navigator.sendBeacon) navigator.sendBeacon("/__vantage_console", body);
725
- else fetch("/__vantage_console", { method: "POST", body, keepalive: true });
726
- } catch (_) {}
727
- };
728
- const fmt = (args) => args.map((a) => {
729
- if (a instanceof Error) return a.stack || a.message;
730
- if (typeof a === "object") { try { return JSON.stringify(a); } catch { return String(a); } }
731
- return String(a);
732
- }).join(" ");
733
- for (const lvl of ["log", "info", "warn", "error"]) {
734
- const orig = console[lvl].bind(console);
735
- console[lvl] = (...args) => { send(lvl, fmt(args)); orig(...args); };
736
- }
737
- window.addEventListener("error", (e) => send("error", (e.message || "Error") + " @ " + (e.filename || "") + ":" + e.lineno));
738
- window.addEventListener("unhandledrejection", (e) => send("error", "UnhandledRejection: " + ((e.reason && e.reason.stack) || e.reason)));
739
- })();
740
- `;
741
- function handleConsoleBeacon(req, res) {
742
- let body = "";
743
- req.on("data", (chunk) => {
744
- body += chunk;
745
- if (body.length > 1e6) req.destroy();
746
- });
747
- req.on("end", () => {
748
- try {
749
- const { level, message } = JSON.parse(body);
750
- const tag = pc.dim(`[browser:${level}]`);
751
- const color = level === "error" ? pc.red : level === "warn" ? pc.yellow : (s) => s;
752
- console.log(`${tag} ${color(String(message))}`);
753
- } catch {
754
- }
755
- res.statusCode = 204;
756
- res.end();
757
- });
758
- }
759
-
760
- export { API_METHODS, FORBIDDEN_FILES, MANIFEST_SCHEMA_VERSION, PAGE_EXTENSIONS, PUBLIC_ENV_PREFIX, VANTAGE_DIR, extractPageMeta, generateArtifacts, scanProject, vantagePlugin };
761
- //# sourceMappingURL=chunk-3QKPNWKJ.js.map
762
- //# sourceMappingURL=chunk-3QKPNWKJ.js.map