@vistagenic/vista 0.3.5 → 0.3.8

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 (74) hide show
  1. package/bin/flashpack-agent-playground.mjs +134 -0
  2. package/bin/flashpack-boot.mjs +504 -0
  3. package/bin/flashpack-ssr-loader.mjs +42 -0
  4. package/bin/flashpack-ssr.mjs +81 -0
  5. package/bin/flashpack-vendor.mjs +97 -0
  6. package/bin/vista.js +56 -42
  7. package/dist/ai/providers/base.js +7 -1
  8. package/dist/bin/build-rsc.js +16 -0
  9. package/dist/bin/build.js +17 -11
  10. package/dist/bin/deploy.js +7 -0
  11. package/dist/bin/dev-client-runtime.d.ts +6 -0
  12. package/dist/bin/dev-client-runtime.js +99 -0
  13. package/dist/bin/dev-error-overlay-snippet.js +79 -21
  14. package/dist/bin/dev-error-overlay-snippet.ts +840 -0
  15. package/dist/bin/devtools-indicator-snippet.js +29 -13
  16. package/dist/bin/file-scanner.js +7 -24
  17. package/dist/bin/generate.js +94 -2
  18. package/dist/bin/webpack.config.js +3 -1
  19. package/dist/build/rsc/compiler.d.ts +14 -0
  20. package/dist/build/rsc/compiler.js +66 -6
  21. package/dist/build/rsc/server-manifest.js +5 -4
  22. package/dist/config.d.ts +5 -0
  23. package/dist/config.js +15 -1
  24. package/dist/deploy/adapters/render.js +2 -2
  25. package/dist/deploy/index.d.ts +1 -0
  26. package/dist/deploy/index.js +3 -0
  27. package/dist/deploy/runtime-pack.js +22 -1
  28. package/dist/deploy/utils.js +16 -6
  29. package/dist/flashpack/command.d.ts +1 -0
  30. package/dist/flashpack/command.js +42 -76
  31. package/dist/flashpack/runtime.d.ts +12 -0
  32. package/dist/flashpack/runtime.js +124 -22
  33. package/dist/metadata/generate.d.ts +1 -0
  34. package/dist/metadata/generate.js +117 -21
  35. package/dist/metadata/index.d.ts +5 -0
  36. package/dist/metadata/index.js +15 -1
  37. package/dist/metadata/json-ld.d.ts +15 -0
  38. package/dist/metadata/json-ld.js +19 -0
  39. package/dist/metadata/merge.d.ts +17 -0
  40. package/dist/metadata/merge.js +161 -0
  41. package/dist/metadata/routes.d.ts +66 -0
  42. package/dist/metadata/routes.js +143 -0
  43. package/dist/metadata/types.d.ts +1 -1
  44. package/dist/metadata/types.js +3 -1
  45. package/dist/og/image-response.d.ts +38 -0
  46. package/dist/og/image-response.js +88 -0
  47. package/dist/og/index.d.ts +2 -0
  48. package/dist/og/index.js +5 -0
  49. package/dist/rust/index.d.ts +13 -0
  50. package/dist/rust/index.js +132 -0
  51. package/dist/server/engine.js +13 -3
  52. package/dist/server/logger.js +2 -1
  53. package/dist/server/project-alias-resolver.d.ts +7 -0
  54. package/dist/server/project-alias-resolver.js +70 -0
  55. package/dist/server/rsc-engine.js +93 -16
  56. package/dist/server/static-generator.js +4 -3
  57. package/dist/server/typed-api-runtime.js +92 -0
  58. package/dist/theme/index.d.ts +1 -0
  59. package/dist/theme/index.js +3 -1
  60. package/dist/theme/react-server.d.ts +4 -2
  61. package/dist/theme/react-server.js +5 -3
  62. package/dist/theme/theme-toggle.d.ts +5 -0
  63. package/dist/theme/theme-toggle.js +39 -0
  64. package/docs/ai.md +69 -0
  65. package/docs/app.md +30 -0
  66. package/docs/cli.md +29 -0
  67. package/docs/index.md +18 -0
  68. package/docs/rag.md +54 -0
  69. package/native/win32-x64/flashpack-cli.exe +0 -0
  70. package/package.json +196 -178
  71. package/skills/README.md +9 -0
  72. package/skills/vista-agent/SKILL.md +28 -0
  73. package/skills/vista-dev-loop/SKILL.md +25 -0
  74. package/skills/vista-rag/SKILL.md +26 -0
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ /**
3
+ * Next-compatible MetadataRoute helpers for robots.txt, sitemap.xml, and web manifests.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.serializeRobots = serializeRobots;
7
+ exports.serializeSitemap = serializeSitemap;
8
+ exports.robots = robots;
9
+ exports.sitemap = sitemap;
10
+ exports.manifest = manifest;
11
+ exports.metadataRouteToResponse = metadataRouteToResponse;
12
+ function asArray(value) {
13
+ if (value === undefined)
14
+ return [];
15
+ return Array.isArray(value) ? value : [value];
16
+ }
17
+ function escapeXml(value) {
18
+ return value
19
+ .replace(/&/g, '&')
20
+ .replace(/</g, '&lt;')
21
+ .replace(/>/g, '&gt;')
22
+ .replace(/"/g, '&quot;')
23
+ .replace(/'/g, '&apos;');
24
+ }
25
+ function formatLastModified(value) {
26
+ if (!value)
27
+ return undefined;
28
+ if (value instanceof Date)
29
+ return value.toISOString();
30
+ return value;
31
+ }
32
+ /** Serialize a Robots object to robots.txt body text. */
33
+ function serializeRobots(data) {
34
+ const rules = asArray(data.rules);
35
+ const lines = [];
36
+ for (const rule of rules) {
37
+ const agents = asArray(rule.userAgent);
38
+ if (agents.length === 0) {
39
+ lines.push('User-agent: *');
40
+ }
41
+ else {
42
+ for (const agent of agents) {
43
+ lines.push(`User-agent: ${agent}`);
44
+ }
45
+ }
46
+ for (const allow of asArray(rule.allow)) {
47
+ lines.push(`Allow: ${allow}`);
48
+ }
49
+ for (const disallow of asArray(rule.disallow)) {
50
+ lines.push(`Disallow: ${disallow}`);
51
+ }
52
+ if (typeof rule.crawlDelay === 'number') {
53
+ lines.push(`Crawl-delay: ${rule.crawlDelay}`);
54
+ }
55
+ if (rule.other) {
56
+ for (const [key, raw] of Object.entries(rule.other)) {
57
+ for (const entry of asArray(raw)) {
58
+ lines.push(`${key}: ${entry}`);
59
+ }
60
+ }
61
+ }
62
+ lines.push('');
63
+ }
64
+ if (data.host) {
65
+ lines.push(`Host: ${data.host}`);
66
+ }
67
+ for (const sitemapUrl of asArray(data.sitemap)) {
68
+ lines.push(`Sitemap: ${sitemapUrl}`);
69
+ }
70
+ return `${lines.join('\n').replace(/\n+$/, '')}\n`;
71
+ }
72
+ /** Serialize sitemap entries to XML. */
73
+ function serializeSitemap(entries) {
74
+ const urls = entries
75
+ .map((entry) => {
76
+ const parts = [`<loc>${escapeXml(entry.url)}</loc>`];
77
+ const lastmod = formatLastModified(entry.lastModified);
78
+ if (lastmod)
79
+ parts.push(`<lastmod>${escapeXml(lastmod)}</lastmod>`);
80
+ if (entry.changeFrequency) {
81
+ parts.push(`<changefreq>${escapeXml(entry.changeFrequency)}</changefreq>`);
82
+ }
83
+ if (typeof entry.priority === 'number') {
84
+ parts.push(`<priority>${entry.priority}</priority>`);
85
+ }
86
+ if (entry.alternates?.languages) {
87
+ for (const [lang, href] of Object.entries(entry.alternates.languages)) {
88
+ parts.push(`<xhtml:link rel="alternate" hreflang="${escapeXml(lang)}" href="${escapeXml(href)}" />`);
89
+ }
90
+ }
91
+ for (const image of entry.images || []) {
92
+ parts.push(`<image:image><image:loc>${escapeXml(image)}</image:loc></image:image>`);
93
+ }
94
+ return `<url>${parts.join('')}</url>`;
95
+ })
96
+ .join('');
97
+ return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1">${urls}</urlset>`;
98
+ }
99
+ const DEFAULT_CACHE = 'public, max-age=0, s-maxage=3600';
100
+ /** Build a robots.txt Response. */
101
+ function robots(data) {
102
+ return new Response(serializeRobots(data), {
103
+ headers: {
104
+ 'Content-Type': 'text/plain; charset=utf-8',
105
+ 'Cache-Control': DEFAULT_CACHE,
106
+ },
107
+ });
108
+ }
109
+ /** Build a sitemap.xml Response. */
110
+ function sitemap(entries) {
111
+ return new Response(serializeSitemap(entries), {
112
+ headers: {
113
+ 'Content-Type': 'application/xml; charset=utf-8',
114
+ 'Cache-Control': DEFAULT_CACHE,
115
+ },
116
+ });
117
+ }
118
+ /** Build a webmanifest JSON Response. */
119
+ function manifest(data) {
120
+ return new Response(JSON.stringify(data, null, 2), {
121
+ headers: {
122
+ 'Content-Type': 'application/manifest+json; charset=utf-8',
123
+ 'Cache-Control': DEFAULT_CACHE,
124
+ },
125
+ });
126
+ }
127
+ /** Convert a MetadataRoute default-export payload into a Response. */
128
+ function metadataRouteToResponse(stem, payload) {
129
+ if (payload instanceof Response)
130
+ return payload;
131
+ if (payload == null)
132
+ return null;
133
+ if (stem === 'robots' && typeof payload === 'object') {
134
+ return robots(payload);
135
+ }
136
+ if (stem === 'sitemap' && Array.isArray(payload)) {
137
+ return sitemap(payload);
138
+ }
139
+ if (stem === 'manifest' && typeof payload === 'object') {
140
+ return manifest(payload);
141
+ }
142
+ return null;
143
+ }
@@ -8,7 +8,7 @@
8
8
  * Template string for title with inheritance support
9
9
  */
10
10
  export type TemplateString = {
11
- default: string;
11
+ default?: string;
12
12
  template?: string;
13
13
  absolute?: string;
14
14
  };
@@ -11,5 +11,7 @@ exports.isTemplateString = isTemplateString;
11
11
  // Export type guard
12
12
  // ============================================================================
13
13
  function isTemplateString(title) {
14
- return typeof title === 'object' && title !== null && 'default' in title;
14
+ if (typeof title !== 'object' || title === null)
15
+ return false;
16
+ return 'default' in title || 'absolute' in title || 'template' in title;
15
17
  }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * ImageResponse — Next/Astro-compatible OG image generation via Satori + resvg.
3
+ */
4
+ import type { ReactElement } from 'react';
5
+ export type ImageResponseOptions = {
6
+ width?: number;
7
+ height?: number;
8
+ status?: number;
9
+ statusText?: string;
10
+ headers?: HeadersInit;
11
+ fonts?: Array<{
12
+ name: string;
13
+ data: ArrayBuffer | Buffer;
14
+ weight?: number;
15
+ style?: 'normal' | 'italic';
16
+ }>;
17
+ debug?: boolean;
18
+ };
19
+ /**
20
+ * Generate a PNG Response from a JSX tree (Satori subset of CSS).
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * import { ImageResponse } from 'vista/og'
25
+ *
26
+ * export default function OpenGraphImage() {
27
+ * return new ImageResponse(
28
+ * <div style={{ fontSize: 64, background: '#111', color: 'white', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
29
+ * Vista
30
+ * </div>,
31
+ * { width: 1200, height: 630 }
32
+ * )
33
+ * }
34
+ * ```
35
+ */
36
+ export declare class ImageResponse extends Response {
37
+ constructor(element: ReactElement, options?: ImageResponseOptions);
38
+ }
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * ImageResponse — Next/Astro-compatible OG image generation via Satori + resvg.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ImageResponse = void 0;
7
+ async function loadSatori() {
8
+ const mod = await import('satori');
9
+ return mod.default || mod;
10
+ }
11
+ async function renderSvgToPng(svg) {
12
+ const resvgMod = await import('@resvg/resvg-wasm');
13
+ const { Resvg, initWasm } = resvgMod;
14
+ if (typeof initWasm === 'function') {
15
+ try {
16
+ // Prefer bundled wasm; fall back silently if already initialized.
17
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
18
+ const wasmUrl = require.resolve('@resvg/resvg-wasm/index_bg.wasm');
19
+ const fs = await import('fs');
20
+ const wasm = fs.readFileSync(wasmUrl);
21
+ await initWasm(wasm);
22
+ }
23
+ catch {
24
+ try {
25
+ await initWasm();
26
+ }
27
+ catch {
28
+ // Already initialized in this process.
29
+ }
30
+ }
31
+ }
32
+ const resvg = new Resvg(svg, {
33
+ fitTo: { mode: 'original' },
34
+ });
35
+ const pngData = resvg.render();
36
+ return pngData.asPng();
37
+ }
38
+ /**
39
+ * Generate a PNG Response from a JSX tree (Satori subset of CSS).
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * import { ImageResponse } from 'vista/og'
44
+ *
45
+ * export default function OpenGraphImage() {
46
+ * return new ImageResponse(
47
+ * <div style={{ fontSize: 64, background: '#111', color: 'white', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
48
+ * Vista
49
+ * </div>,
50
+ * { width: 1200, height: 630 }
51
+ * )
52
+ * }
53
+ * ```
54
+ */
55
+ class ImageResponse extends Response {
56
+ constructor(element, options = {}) {
57
+ const width = options.width ?? 1200;
58
+ const height = options.height ?? 630;
59
+ const status = options.status ?? 200;
60
+ const statusText = options.statusText;
61
+ const headers = new Headers(options.headers);
62
+ headers.set('Content-Type', 'image/png');
63
+ if (!headers.has('Cache-Control')) {
64
+ headers.set('Cache-Control', 'public, immutable, no-transform, max-age=31536000');
65
+ }
66
+ const body = new ReadableStream({
67
+ async start(controller) {
68
+ try {
69
+ const satori = await loadSatori();
70
+ const svg = await satori(element, {
71
+ width,
72
+ height,
73
+ fonts: options.fonts,
74
+ embedFont: true,
75
+ });
76
+ const png = await renderSvgToPng(svg);
77
+ controller.enqueue(png);
78
+ controller.close();
79
+ }
80
+ catch (error) {
81
+ controller.error(error);
82
+ }
83
+ },
84
+ });
85
+ super(body, { status, statusText, headers });
86
+ }
87
+ }
88
+ exports.ImageResponse = ImageResponse;
@@ -0,0 +1,2 @@
1
+ export { ImageResponse } from './image-response';
2
+ export type { ImageResponseOptions } from './image-response';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ImageResponse = void 0;
4
+ var image_response_1 = require("./image-response");
5
+ Object.defineProperty(exports, "ImageResponse", { enumerable: true, get: function () { return image_response_1.ImageResponse; } });
@@ -0,0 +1,13 @@
1
+ export interface ClassifiedSegment {
2
+ kind: string;
3
+ segment: string;
4
+ }
5
+ export declare function nativeBindingsLoaded(): boolean;
6
+ export declare function classifyAppSegment(folder: string): ClassifiedSegment;
7
+ export declare function routePattern(folders: string[]): string;
8
+ export declare function encodeVistaErrorCode(code: string): string;
9
+ export declare function findVistaErrorCodes(source: string): string[];
10
+ export declare function tasklessSteps(enabled: boolean): string[];
11
+ export declare function classifyAppSegmentFallback(folder: string): ClassifiedSegment;
12
+ export declare function routePatternFallback(folders: string[]): string;
13
+ export declare function readBoundPipeline(cwd?: string): Record<string, unknown> | null;
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.nativeBindingsLoaded = nativeBindingsLoaded;
7
+ exports.classifyAppSegment = classifyAppSegment;
8
+ exports.routePattern = routePattern;
9
+ exports.encodeVistaErrorCode = encodeVistaErrorCode;
10
+ exports.findVistaErrorCodes = findVistaErrorCodes;
11
+ exports.tasklessSteps = tasklessSteps;
12
+ exports.classifyAppSegmentFallback = classifyAppSegmentFallback;
13
+ exports.routePatternFallback = routePatternFallback;
14
+ exports.readBoundPipeline = readBoundPipeline;
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
17
+ let native;
18
+ function loadNative() {
19
+ if (native !== undefined)
20
+ return native;
21
+ const candidates = [
22
+ path_1.default.resolve(__dirname, '../../../crates/vista-napi'),
23
+ path_1.default.resolve(__dirname, '../../../../crates/vista-napi'),
24
+ path_1.default.resolve(process.cwd(), 'crates/vista-napi'),
25
+ path_1.default.resolve(process.cwd(), '../crates/vista-napi'),
26
+ ];
27
+ for (const candidate of candidates) {
28
+ try {
29
+ native = require(candidate);
30
+ return native;
31
+ }
32
+ catch {
33
+ // Try the next location. The TypeScript fallback stays correct either way.
34
+ }
35
+ }
36
+ native = null;
37
+ return null;
38
+ }
39
+ function nativeBindingsLoaded() {
40
+ return loadNative() !== null;
41
+ }
42
+ function classifyAppSegment(folder) {
43
+ const bound = loadNative();
44
+ if (bound?.classifyAppSegmentInfo)
45
+ return bound.classifyAppSegmentInfo(folder);
46
+ return classifyAppSegmentFallback(folder);
47
+ }
48
+ function routePattern(folders) {
49
+ const bound = loadNative();
50
+ if (bound?.routePattern)
51
+ return bound.routePattern(folders);
52
+ return routePatternFallback(folders);
53
+ }
54
+ function isValidCode(code) {
55
+ return code.length > 0 && /^[A-Z0-9_]+$/.test(code);
56
+ }
57
+ function encodeVistaErrorCode(code) {
58
+ const bound = loadNative();
59
+ if (bound?.encodeVistaErrorCode)
60
+ return bound.encodeVistaErrorCode(code);
61
+ if (code.startsWith('VISTA_') && isValidCode(code.slice('VISTA_'.length)))
62
+ return code;
63
+ return `VISTA_${code}`;
64
+ }
65
+ function findVistaErrorCodes(source) {
66
+ const bound = loadNative();
67
+ if (bound?.findVistaErrorCodes)
68
+ return bound.findVistaErrorCodes(source);
69
+ const found = [];
70
+ const pattern = /VISTA_([A-Z0-9_]+)/g;
71
+ for (const match of source.matchAll(pattern)) {
72
+ if (!found.includes(match[1]))
73
+ found.push(match[1]);
74
+ }
75
+ return found;
76
+ }
77
+ function tasklessSteps(enabled) {
78
+ const bound = loadNative();
79
+ if (bound?.tasklessSteps)
80
+ return bound.tasklessSteps(enabled);
81
+ return enabled ? ['scan', 'reuse-state', 'serve'] : ['scan', 'queue-work', 'serve'];
82
+ }
83
+ function classifyAppSegmentFallback(folder) {
84
+ if (!folder)
85
+ return { kind: 'static', segment: '' };
86
+ if (folder.startsWith('(...)') ||
87
+ folder.startsWith('(..)(..)') ||
88
+ folder.startsWith('(..)') ||
89
+ folder.startsWith('(.)')) {
90
+ return { kind: 'interception', segment: folder };
91
+ }
92
+ if (folder.startsWith('(') && folder.endsWith(')') && folder.length >= 2) {
93
+ return { kind: 'group', segment: folder.slice(1, -1) };
94
+ }
95
+ if (folder.startsWith('@') && folder.length > 1) {
96
+ return { kind: 'parallel', segment: folder.slice(1) };
97
+ }
98
+ if (folder.startsWith('[[...') && folder.endsWith(']]') && folder.length > '[[...]]'.length) {
99
+ return { kind: 'optional-catch-all', segment: folder.slice(5, -2) };
100
+ }
101
+ if (folder.startsWith('[...') && folder.endsWith(']') && folder.length > '[...]'.length) {
102
+ return { kind: 'catch-all', segment: folder.slice(4, -1) };
103
+ }
104
+ if (folder.startsWith('[') && folder.endsWith(']') && folder.length >= 2) {
105
+ return { kind: 'dynamic', segment: folder.slice(1, -1) };
106
+ }
107
+ return { kind: 'static', segment: folder };
108
+ }
109
+ function routePatternFallback(folders) {
110
+ const parts = [];
111
+ for (const folder of folders) {
112
+ const classified = classifyAppSegmentFallback(folder);
113
+ if (classified.kind === 'group' || classified.kind === 'parallel' || classified.kind === 'interception') {
114
+ continue;
115
+ }
116
+ if (classified.kind === 'dynamic')
117
+ parts.push(`:${classified.segment}`);
118
+ else if (classified.kind === 'catch-all')
119
+ parts.push(`:${classified.segment}*`);
120
+ else if (classified.kind === 'optional-catch-all')
121
+ parts.push(`:${classified.segment}*?`);
122
+ else
123
+ parts.push(classified.segment);
124
+ }
125
+ return parts.length === 0 ? '/' : `/${parts.join('/')}`;
126
+ }
127
+ function readBoundPipeline(cwd = process.cwd()) {
128
+ const file = path_1.default.join(cwd, '.flash', 'pipeline', 'bound.json');
129
+ if (!fs_1.default.existsSync(file))
130
+ return null;
131
+ return JSON.parse(fs_1.default.readFileSync(file, 'utf8'));
132
+ }
@@ -341,6 +341,15 @@ function startServer(port = 3003, compiler) {
341
341
  })
342
342
  .filter((entry) => entry.trim().length > 0);
343
343
  (0, logger_1.logEvent)('Build error detected, pushing to browser...');
344
+ console.error('');
345
+ (0, logger_1.logError)(`Build failed with ${errorMessages.length} error${errorMessages.length === 1 ? '' : 's'}`);
346
+ for (const message of errorMessages) {
347
+ const text = message.replace(/\u001b\[[0-9;]*m/g, '').trim();
348
+ if (!text)
349
+ continue;
350
+ console.error(text);
351
+ console.error('');
352
+ }
344
353
  pushCompileError(errorMessages);
345
354
  }
346
355
  else {
@@ -588,22 +597,23 @@ function startServer(port = 3003, compiler) {
588
597
  const pageDir = path_1.default.dirname(pagePath);
589
598
  const layouts = (0, root_resolver_1.resolveLayoutChain)(cwd, pageDir, isDev);
590
599
  // Metadata extraction - merge all layout metadata + page metadata
600
+ const { deepMergeMetadata } = require('../metadata/merge');
591
601
  let metadata = {};
592
602
  for (const layout of layouts) {
593
603
  if (layout.metadata) {
594
- metadata = { ...metadata, ...layout.metadata };
604
+ metadata = deepMergeMetadata(metadata, layout.metadata);
595
605
  }
596
606
  }
597
607
  // Get page static metadata (overrides layouts)
598
608
  if (PageModule.metadata) {
599
- metadata = { ...metadata, ...PageModule.metadata };
609
+ metadata = deepMergeMetadata(metadata, PageModule.metadata);
600
610
  }
601
611
  // Get dynamic metadata from generateMetadata function
602
612
  if (typeof PageModule.generateMetadata === 'function') {
603
613
  try {
604
614
  const dynamicMeta = await PageModule.generateMetadata({ params, searchParams: req.query }, metadata // parent metadata
605
615
  );
606
- metadata = { ...metadata, ...dynamicMeta };
616
+ metadata = deepMergeMetadata(metadata, dynamicMeta);
607
617
  }
608
618
  catch (e) {
609
619
  console.error('Error in generateMetadata:', e);
@@ -99,7 +99,8 @@ function printServerReady(info) {
99
99
  const localUrl = `http://localhost:${port}`;
100
100
  const networkUrl = networkAddr ? `http://${networkAddr}:${port}` : null;
101
101
  console.log(VISTA_BANNER);
102
- console.log(` ${green('▼')} ${bold('Vista.Js')} ${dim(`v${getVistaVersion()}`)}`);
102
+ const engineLabel = process.env.VISTA_ENGINE === 'flashpack' ? ` ${cyan('(flashpack)')}` : '';
103
+ console.log(` ${green('▼')} ${bold('Vista.Js')} ${dim(`v${getVistaVersion()}`)}${engineLabel}`);
103
104
  console.log('');
104
105
  console.log(` ${dim('┃')} ${bold('Local:')} ${cyan(localUrl)}`);
105
106
  if (networkUrl) {
@@ -2,3 +2,10 @@ export interface ProjectAliasResolver {
2
2
  resolve: (request: string) => string | null;
3
3
  }
4
4
  export declare function createProjectAliasResolver(cwd: string, resolveFromWorkspace: (specifier: string, cwd: string) => string): ProjectAliasResolver | null;
5
+ /**
6
+ * Webpack resolve.alias entries for the project's tsconfig/jsconfig `paths`.
7
+ * Wildcard `@/*` becomes the prefix alias `@` so client bundles resolve `@/data/site`.
8
+ */
9
+ export declare function loadProjectWebpackAliases(cwd: string): Record<string, string>;
10
+ /** True when a module request is covered by a webpack path alias. */
11
+ export declare function requestUsesProjectAlias(request: string, aliases: Record<string, string>): boolean;
@@ -4,6 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createProjectAliasResolver = createProjectAliasResolver;
7
+ exports.loadProjectWebpackAliases = loadProjectWebpackAliases;
8
+ exports.requestUsesProjectAlias = requestUsesProjectAlias;
7
9
  const fs_1 = __importDefault(require("fs"));
8
10
  const path_1 = __importDefault(require("path"));
9
11
  const projectAliasCache = new Map();
@@ -215,3 +217,71 @@ function createProjectAliasResolver(cwd, resolveFromWorkspace) {
215
217
  projectAliasCache.set(cwd, resolver);
216
218
  return resolver;
217
219
  }
220
+ /**
221
+ * Webpack resolve.alias entries for the project's tsconfig/jsconfig `paths`.
222
+ * Wildcard `@/*` becomes the prefix alias `@` so client bundles resolve `@/data/site`.
223
+ */
224
+ function loadProjectWebpackAliases(cwd) {
225
+ const configPath = ['tsconfig.json', 'jsconfig.json']
226
+ .map((filename) => path_1.default.join(cwd, filename))
227
+ .find((filename) => fs_1.default.existsSync(filename));
228
+ if (!configPath)
229
+ return {};
230
+ let compilerOptions = null;
231
+ try {
232
+ const rawConfig = fs_1.default.readFileSync(configPath, 'utf-8');
233
+ const parsedConfig = JSON.parse(stripJsonComments(rawConfig));
234
+ compilerOptions = parsedConfig.compilerOptions ?? null;
235
+ }
236
+ catch {
237
+ return {};
238
+ }
239
+ const rawPaths = compilerOptions?.paths;
240
+ if (!rawPaths || typeof rawPaths !== 'object')
241
+ return {};
242
+ const configDir = path_1.default.dirname(configPath);
243
+ const baseDir = path_1.default.resolve(configDir, typeof compilerOptions?.baseUrl === 'string' && compilerOptions.baseUrl.trim()
244
+ ? compilerOptions.baseUrl
245
+ : '.');
246
+ const aliases = {};
247
+ for (const [pattern, targetsValue] of Object.entries(rawPaths)) {
248
+ const targets = Array.isArray(targetsValue)
249
+ ? targetsValue.filter((entry) => typeof entry === 'string' && entry.trim().length > 0)
250
+ : [];
251
+ const target = targets[0];
252
+ if (!target)
253
+ continue;
254
+ if (pattern.includes('*')) {
255
+ const starIndex = pattern.indexOf('*');
256
+ const requestPrefix = pattern.slice(0, starIndex);
257
+ const targetStar = target.indexOf('*');
258
+ const targetPrefix = targetStar === -1 ? target : target.slice(0, targetStar);
259
+ if (!requestPrefix)
260
+ continue;
261
+ // enhanced-resolve matches `name` or `${name}/`. A tsconfig prefix of
262
+ // `@/` must be registered as `@`, or the resolver looks for `@//`.
263
+ const aliasName = requestPrefix.endsWith('/')
264
+ ? requestPrefix.slice(0, -1)
265
+ : requestPrefix;
266
+ if (!aliasName)
267
+ continue;
268
+ // The matched remainder already begins with `/`. path.resolve drops a
269
+ // trailing separator, so do not add one back.
270
+ aliases[aliasName] = path_1.default.resolve(baseDir, targetPrefix);
271
+ continue;
272
+ }
273
+ const resolved = resolveAliasTargetPath(path_1.default.resolve(baseDir, target));
274
+ if (resolved) {
275
+ aliases[pattern] = resolved;
276
+ }
277
+ }
278
+ return aliases;
279
+ }
280
+ /** True when a module request is covered by a webpack path alias. */
281
+ function requestUsesProjectAlias(request, aliases) {
282
+ for (const key of Object.keys(aliases)) {
283
+ if (request === key || request.startsWith(`${key}/`))
284
+ return true;
285
+ }
286
+ return false;
287
+ }