@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aravindc26/velu",
3
- "version": "0.13.3",
3
+ "version": "0.13.5",
4
4
  "description": "A modern documentation site generator powered by Markdown and JSON configuration",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,3 +1,5 @@
1
+ export const dynamic = 'force-dynamic';
2
+
1
3
  import { createElement, type ReactNode } from 'react';
2
4
  import { loadSessionConfigSource } from '@/lib/preview-config';
3
5
  import {
@@ -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) notFound();
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
- // Dynamic/async entry compile MDX on demand
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') notFound();
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
 
@@ -1,3 +1,5 @@
1
+ export const dynamic = 'force-dynamic';
2
+
1
3
  import { redirect } from 'next/navigation';
2
4
  import { getSessionPageTree } from '@/lib/preview-source';
3
5
 
@@ -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] Init failed for session ${sessionId}:`, message);
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] Sync failed for session ${sessionId}, file ${file}:`, message);
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
- } catch {
796
- // Fall through to direct copy
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) return cached.source;
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
- // Return an empty source if no content yet
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
  /**