@aravindc26/velu 0.13.3 → 0.13.5
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.
- package/package.json +1 -1
- package/src/engine/app/_preview/[sessionId]/[...slug]/layout.tsx +2 -0
- package/src/engine/app/_preview/[sessionId]/[...slug]/page.tsx +17 -3
- package/src/engine/app/_preview/[sessionId]/page.tsx +2 -0
- package/src/engine/app/_preview/api/sessions/[sessionId]/init/route.ts +5 -1
- package/src/engine/app/_preview/api/sessions/[sessionId]/sync/route.ts +5 -1
- package/src/engine/lib/preview-content.ts +16 -2
- package/src/engine/lib/preview-source.ts +34 -3
- package/src/engine/tsconfig.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -23,6 +23,9 @@ import { OpenApiTocSync } from '@/components/openapi-toc-sync';
|
|
|
23
23
|
import { TocExamples } from '@/components/toc-examples';
|
|
24
24
|
import { VeluIcon } from '@/components/icon';
|
|
25
25
|
|
|
26
|
+
// Disable Next.js route caching — content changes on every sync
|
|
27
|
+
export const dynamic = 'force-dynamic';
|
|
28
|
+
|
|
26
29
|
interface PageProps {
|
|
27
30
|
params: Promise<{ sessionId: string; slug: string[] }>;
|
|
28
31
|
}
|
|
@@ -138,10 +141,15 @@ function prefixMdxComponentLinks(
|
|
|
138
141
|
export default async function PreviewPage({ params }: PageProps) {
|
|
139
142
|
const { sessionId, slug } = await params;
|
|
140
143
|
|
|
144
|
+
console.log(`[PREVIEW:page] START session=${sessionId} slug=${slug.join('/')}`);
|
|
145
|
+
|
|
141
146
|
const src = await getSessionSource(sessionId);
|
|
142
147
|
const page = src.getPage(slug);
|
|
143
148
|
|
|
144
|
-
if (!page)
|
|
149
|
+
if (!page) {
|
|
150
|
+
console.log(`[PREVIEW:page] Page NOT FOUND for slug=${slug.join('/')}`);
|
|
151
|
+
notFound();
|
|
152
|
+
}
|
|
145
153
|
|
|
146
154
|
const pageDataRecord = page.data as unknown as Record<string, unknown>;
|
|
147
155
|
|
|
@@ -151,17 +159,22 @@ export default async function PreviewPage({ params }: PageProps) {
|
|
|
151
159
|
let pageToc: any;
|
|
152
160
|
|
|
153
161
|
if (typeof loadFn === 'function') {
|
|
154
|
-
|
|
162
|
+
console.log(`[PREVIEW:page] Calling load() for slug=${slug.join('/')}`);
|
|
155
163
|
const loaded = await loadFn();
|
|
156
164
|
MDX = loaded.body;
|
|
157
165
|
pageToc = loaded.toc;
|
|
166
|
+
console.log(`[PREVIEW:page] load() returned MDX=${typeof MDX} toc=${typeof pageToc}`);
|
|
158
167
|
} else {
|
|
159
168
|
// Fallback: pre-compiled entry (shouldn't happen in preview mode, but safe)
|
|
169
|
+
console.log(`[PREVIEW:page] No load() — using pre-compiled body for slug=${slug.join('/')}`);
|
|
160
170
|
MDX = pageDataRecord.body as any;
|
|
161
171
|
pageToc = pageDataRecord.toc as any;
|
|
162
172
|
}
|
|
163
173
|
|
|
164
|
-
if (typeof MDX !== 'function')
|
|
174
|
+
if (typeof MDX !== 'function') {
|
|
175
|
+
console.log(`[PREVIEW:page] MDX is not a function (type=${typeof MDX}), returning 404`);
|
|
176
|
+
notFound();
|
|
177
|
+
}
|
|
165
178
|
|
|
166
179
|
const configSource = loadSessionConfigSource(sessionId);
|
|
167
180
|
const footerSocials = configSource ? getFooterSocials(configSource) : [];
|
|
@@ -173,6 +186,7 @@ export default async function PreviewPage({ params }: PageProps) {
|
|
|
173
186
|
if (pageInfo?.fullPath) {
|
|
174
187
|
try {
|
|
175
188
|
effectiveMarkdown = readFileSync(pageInfo.fullPath, 'utf-8');
|
|
189
|
+
console.log(`[PREVIEW:page] Read markdown from ${pageInfo.fullPath} (${effectiveMarkdown.length} chars, first 120: ${JSON.stringify(effectiveMarkdown.slice(0, 120))})`);
|
|
176
190
|
} catch { /* file may not exist */ }
|
|
177
191
|
}
|
|
178
192
|
|
|
@@ -11,12 +11,16 @@ export async function POST(
|
|
|
11
11
|
|
|
12
12
|
const { sessionId } = await params;
|
|
13
13
|
|
|
14
|
+
console.log(`[PREVIEW:init] START session=${sessionId}`);
|
|
15
|
+
|
|
14
16
|
try {
|
|
15
17
|
const result = generateSessionContent(sessionId);
|
|
18
|
+
console.log(`[PREVIEW:init] generateSessionContent result:`, JSON.stringify(result));
|
|
16
19
|
|
|
17
20
|
// Invalidate the cached dynamic source so the next page request
|
|
18
21
|
// re-scans the content directory and picks up the new files.
|
|
19
22
|
invalidateSessionSource(sessionId);
|
|
23
|
+
console.log(`[PREVIEW:init] DONE session=${sessionId}`);
|
|
20
24
|
|
|
21
25
|
return Response.json({
|
|
22
26
|
status: 'ready',
|
|
@@ -26,7 +30,7 @@ export async function POST(
|
|
|
26
30
|
});
|
|
27
31
|
} catch (error) {
|
|
28
32
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
29
|
-
console.error(`[PREVIEW]
|
|
33
|
+
console.error(`[PREVIEW:init] FAILED session=${sessionId}:`, message);
|
|
30
34
|
return Response.json(
|
|
31
35
|
{ status: 'error', error: message },
|
|
32
36
|
{ status: 500 },
|
|
@@ -12,6 +12,8 @@ export async function POST(
|
|
|
12
12
|
const { sessionId } = await params;
|
|
13
13
|
const file = request.nextUrl.searchParams.get('file');
|
|
14
14
|
|
|
15
|
+
console.log(`[PREVIEW:sync] START session=${sessionId} file=${file}`);
|
|
16
|
+
|
|
15
17
|
if (!file) {
|
|
16
18
|
return Response.json(
|
|
17
19
|
{ error: 'Missing "file" query parameter' },
|
|
@@ -21,9 +23,11 @@ export async function POST(
|
|
|
21
23
|
|
|
22
24
|
try {
|
|
23
25
|
const result = syncSessionFile(sessionId, file);
|
|
26
|
+
console.log(`[PREVIEW:sync] syncSessionFile result:`, JSON.stringify(result));
|
|
24
27
|
|
|
25
28
|
// Invalidate cached source so next page request re-scans content
|
|
26
29
|
invalidateSessionSource(sessionId);
|
|
30
|
+
console.log(`[PREVIEW:sync] DONE session=${sessionId} file=${file}`);
|
|
27
31
|
|
|
28
32
|
return Response.json({
|
|
29
33
|
status: 'synced',
|
|
@@ -32,7 +36,7 @@ export async function POST(
|
|
|
32
36
|
});
|
|
33
37
|
} catch (error) {
|
|
34
38
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
35
|
-
console.error(`[PREVIEW]
|
|
39
|
+
console.error(`[PREVIEW:sync] FAILED session=${sessionId} file=${file}:`, message);
|
|
36
40
|
return Response.json(
|
|
37
41
|
{ status: 'error', error: message },
|
|
38
42
|
{ status: 500 },
|
|
@@ -755,7 +755,10 @@ export function syncSessionFile(
|
|
|
755
755
|
const workspaceDir = join(WORKSPACE_DIR, sessionId);
|
|
756
756
|
const outputDir = join(PREVIEW_CONTENT_DIR, sessionId);
|
|
757
757
|
|
|
758
|
+
console.log(`[PREVIEW:syncFile] session=${sessionId} file=${filePath} workspace=${workspaceDir} output=${outputDir}`);
|
|
759
|
+
|
|
758
760
|
if (filePath === PRIMARY_CONFIG_NAME || filePath === LEGACY_CONFIG_NAME) {
|
|
761
|
+
console.log(`[PREVIEW:syncFile] Config file changed — regenerating all content`);
|
|
759
762
|
generateSessionContent(sessionId);
|
|
760
763
|
return { synced: true };
|
|
761
764
|
}
|
|
@@ -777,9 +780,13 @@ export function syncSessionFile(
|
|
|
777
780
|
srcPath = join(workspaceDir, `${stripped}.md`);
|
|
778
781
|
}
|
|
779
782
|
if (!existsSync(srcPath)) {
|
|
783
|
+
console.log(`[PREVIEW:syncFile] Source file NOT FOUND at any candidate path for ${filePath}`);
|
|
780
784
|
return { synced: false };
|
|
781
785
|
}
|
|
782
786
|
|
|
787
|
+
const srcContent = readFileSync(srcPath, 'utf-8');
|
|
788
|
+
console.log(`[PREVIEW:syncFile] Source file: ${srcPath} (${srcContent.length} chars, first 120: ${JSON.stringify(srcContent.slice(0, 120))})`);
|
|
789
|
+
|
|
783
790
|
try {
|
|
784
791
|
const { config } = loadConfig(workspaceDir);
|
|
785
792
|
const artifacts = buildArtifacts(config, workspaceDir);
|
|
@@ -789,15 +796,22 @@ export function syncSessionFile(
|
|
|
789
796
|
|
|
790
797
|
if (mapping) {
|
|
791
798
|
const destPath = join(outputDir, `${mapping.dest}.mdx`);
|
|
799
|
+
console.log(`[PREVIEW:syncFile] Mapped: ${stripped} -> ${mapping.dest}, writing to ${destPath}`);
|
|
792
800
|
processPage(srcPath, destPath, stripped, variables, sessionId);
|
|
801
|
+
const written = readFileSync(destPath, 'utf-8');
|
|
802
|
+
console.log(`[PREVIEW:syncFile] Written dest (${written.length} chars, first 120: ${JSON.stringify(written.slice(0, 120))})`);
|
|
793
803
|
return { synced: true };
|
|
794
804
|
}
|
|
795
|
-
|
|
796
|
-
|
|
805
|
+
console.log(`[PREVIEW:syncFile] No mapping found for ${stripped} in pageMap (${artifacts.pageMap.length} entries)`);
|
|
806
|
+
} catch (err) {
|
|
807
|
+
console.log(`[PREVIEW:syncFile] Mapping lookup failed, falling through to direct copy:`, err instanceof Error ? err.message : err);
|
|
797
808
|
}
|
|
798
809
|
|
|
799
810
|
const destPath = join(outputDir, `${stripped}.mdx`);
|
|
811
|
+
console.log(`[PREVIEW:syncFile] Direct copy: ${srcPath} -> ${destPath}`);
|
|
800
812
|
processPage(srcPath, destPath, stripped, variables, sessionId);
|
|
813
|
+
const written = readFileSync(destPath, 'utf-8');
|
|
814
|
+
console.log(`[PREVIEW:syncFile] Written dest (${written.length} chars, first 120: ${JSON.stringify(written.slice(0, 120))})`);
|
|
801
815
|
return { synced: true };
|
|
802
816
|
}
|
|
803
817
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Used only in preview mode (PREVIEW_MODE=true) — production builds still use
|
|
10
10
|
* the standard `source.ts` with build-time collections.
|
|
11
11
|
*/
|
|
12
|
-
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
12
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
13
13
|
import { join, relative } from 'node:path';
|
|
14
14
|
import { loader } from 'fumadocs-core/source';
|
|
15
15
|
import { dynamic } from 'fumadocs-mdx/runtime/dynamic';
|
|
@@ -24,6 +24,11 @@ import * as sourceConfigExports from '../source.config';
|
|
|
24
24
|
|
|
25
25
|
const PREVIEW_CONTENT_DIR = process.env.PREVIEW_CONTENT_DIR || './content';
|
|
26
26
|
|
|
27
|
+
function log(tag: string, msg: string, data?: Record<string, unknown>) {
|
|
28
|
+
const payload = data ? ' ' + JSON.stringify(data) : '';
|
|
29
|
+
console.log(`[PREVIEW:${tag}] ${msg}${payload}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
// ── Cache ──────────────────────────────────────────────────────────────────
|
|
28
33
|
|
|
29
34
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -37,6 +42,7 @@ async function getDynamic() {
|
|
|
37
42
|
if (dynamicInstance) return dynamicInstance;
|
|
38
43
|
if (dynamicInitPromise) return dynamicInitPromise;
|
|
39
44
|
|
|
45
|
+
log('dynamic', 'Initializing new dynamic() instance');
|
|
40
46
|
dynamicInitPromise = dynamic(
|
|
41
47
|
sourceConfigExports,
|
|
42
48
|
{
|
|
@@ -47,6 +53,7 @@ async function getDynamic() {
|
|
|
47
53
|
).then((inst) => {
|
|
48
54
|
dynamicInstance = inst;
|
|
49
55
|
dynamicInitPromise = null;
|
|
56
|
+
log('dynamic', 'dynamic() instance ready');
|
|
50
57
|
return inst;
|
|
51
58
|
});
|
|
52
59
|
|
|
@@ -146,11 +153,16 @@ function scanContentDir(sessionDir: string): {
|
|
|
146
153
|
*/
|
|
147
154
|
export async function getSessionSource(sessionId: string) {
|
|
148
155
|
const cached = sourceCache.get(sessionId);
|
|
149
|
-
if (cached)
|
|
156
|
+
if (cached) {
|
|
157
|
+
const age = Date.now() - cached.createdAt;
|
|
158
|
+
log('source', `Cache HIT for session ${sessionId}`, { ageMs: age });
|
|
159
|
+
return cached.source;
|
|
160
|
+
}
|
|
161
|
+
log('source', `Cache MISS for session ${sessionId}`);
|
|
150
162
|
|
|
151
163
|
const sessionDir = join(PREVIEW_CONTENT_DIR, sessionId);
|
|
152
164
|
if (!existsSync(sessionDir)) {
|
|
153
|
-
|
|
165
|
+
log('source', `Session dir does not exist: ${sessionDir}`);
|
|
154
166
|
const emptySource = loader({
|
|
155
167
|
baseUrl: '/',
|
|
156
168
|
source: { files: [] },
|
|
@@ -161,6 +173,20 @@ export async function getSessionSource(sessionId: string) {
|
|
|
161
173
|
const dyn = await getDynamic();
|
|
162
174
|
const { entries, metaFiles } = scanContentDir(sessionDir);
|
|
163
175
|
|
|
176
|
+
log('source', `Scanned ${sessionDir}`, {
|
|
177
|
+
entryCount: entries.length,
|
|
178
|
+
metaFileCount: Object.keys(metaFiles).length,
|
|
179
|
+
files: entries.map((e) => {
|
|
180
|
+
const stat = statSync(e.info.fullPath, { throwIfNoEntry: false });
|
|
181
|
+
return {
|
|
182
|
+
path: e.info.path,
|
|
183
|
+
title: e.data.title,
|
|
184
|
+
sizeBytes: stat?.size,
|
|
185
|
+
mtimeMs: stat?.mtimeMs,
|
|
186
|
+
};
|
|
187
|
+
}),
|
|
188
|
+
});
|
|
189
|
+
|
|
164
190
|
const collection = await dyn.docs('docs', sessionDir, metaFiles, entries);
|
|
165
191
|
const fumadocsSource = collection.toFumadocsSource();
|
|
166
192
|
|
|
@@ -188,6 +214,9 @@ export async function getSessionSource(sessionId: string) {
|
|
|
188
214
|
});
|
|
189
215
|
|
|
190
216
|
sourceCache.set(sessionId, { source: src, createdAt: Date.now() });
|
|
217
|
+
log('source', `Built and cached source for session ${sessionId}`, {
|
|
218
|
+
pageCount: src.getPages().length,
|
|
219
|
+
});
|
|
191
220
|
return src;
|
|
192
221
|
}
|
|
193
222
|
|
|
@@ -196,7 +225,9 @@ export async function getSessionSource(sessionId: string) {
|
|
|
196
225
|
* Call after content generation or sync so the next request re-scans.
|
|
197
226
|
*/
|
|
198
227
|
export function invalidateSessionSource(sessionId: string): void {
|
|
228
|
+
const had = sourceCache.has(sessionId);
|
|
199
229
|
sourceCache.delete(sessionId);
|
|
230
|
+
log('invalidate', `session=${sessionId} hadCache=${had} remainingKeys=[${[...sourceCache.keys()].join(',')}]`);
|
|
200
231
|
}
|
|
201
232
|
|
|
202
233
|
/**
|