import { readdir, stat } from 'node:fs/promises'; import { join } from 'node:path'; import { assemble } from "../cli/utils/assemble.js"; import { render } from "../render/index.js"; /** * Resolve a DocumentQuery to a path string */ function resolveQueryPath(query) { if (typeof query === 'string') return query; if (typeof query === 'number') return null; if ('path' in query && query.path) return query.path; return null; } /** * Create a resolveDocumentPath function from a DiscoveryResult */ function createResolver(discovery) { return async (path) => { const doc = discovery.documents.get(path); return doc?.fsPath ?? null; }; } /** * Scan a local uploads/ directory and return Upload objects */ async function scanUploads(fsPath) { const uploadsDir = join(fsPath, 'uploads'); try { const files = await readdir(uploadsDir); const uploads = []; for (const filename of files) { if (filename.startsWith('.')) continue; const fileStat = await stat(join(uploadsDir, filename)).catch(() => null); if (!fileStat || !fileStat.isFile()) continue; uploads.push({ id: 0, filename, document_id: 0, created_at: fileStat.mtime, original_filename: filename, hidden: false, hash: '', }); } return uploads; } catch { return []; } } /** * Filesystem-based function context that reads pages from local discovery. * Drop-in replacement for functionContextClient when serving locally. */ export const functionContextFs = (getDiscovery) => { return { getPage: async (query) => { const path = resolveQueryPath(query); if (!path) return null; const discovery = getDiscovery(); const doc = discovery.documents.get(path); if (!doc) return null; const resolveDocumentPath = createResolver(discovery); const document = await assemble(doc.fsPath, { resolveDocumentPath }); return await render(document); }, getPages: async (options) => { const discovery = getDiscovery(); let paths = discovery.sortedPaths; // Filter by startsWithPath if (options?.startsWithPath) { paths = paths.filter(p => p.startsWith(options.startsWithPath)); } // Filter by excludePaths if (options?.excludePaths && options.excludePaths.length > 0) { const excluded = new Set(options.excludePaths); paths = paths.filter(p => !excluded.has(p)); } const resolveDocumentPath = createResolver(discovery); // Assemble and render all matching documents const rendered = []; for (const path of paths) { const info = discovery.documents.get(path); if (!info) continue; try { const doc = await assemble(info.fsPath, { resolveDocumentPath }); rendered.push(await render(doc)); } catch { // Skip documents that fail to assemble } } // Sort if (options?.sortBy) { const order = options.sortOrder === 'desc' ? -1 : 1; const getSortValue = (doc) => { switch (options.sortBy) { case 'created_at': return doc.meta.createdAt; case 'updated_at': return doc.meta.updatedAt; case 'title': return doc.title; case 'path': return doc.path; default: return undefined; } }; rendered.sort((a, b) => { const aVal = getSortValue(a); const bVal = getSortValue(b); if (aVal instanceof Date && bVal instanceof Date) { return (aVal.getTime() - bVal.getTime()) * order; } if (typeof aVal === 'string' && typeof bVal === 'string') { return aVal.localeCompare(bVal) * order; } return 0; }); } // Pagination const offset = options?.offset ?? 0; const limit = options?.limit ?? rendered.length; return rendered.slice(offset, offset + limit); }, getUploads: async (options) => { const discovery = getDiscovery(); let uploads = []; if (options?.path) { const doc = discovery.documents.get(options.path); if (doc) { uploads = await scanUploads(doc.fsPath); } } else { // Scan all documents for uploads for (const [, doc] of discovery.documents) { const docUploads = await scanUploads(doc.fsPath); uploads.push(...docUploads); } } // Sort if (options?.sortBy) { const order = options.sortOrder === 'desc' ? -1 : 1; uploads.sort((a, b) => { const aVal = a[options.sortBy]; const bVal = b[options.sortBy]; if (aVal instanceof Date && bVal instanceof Date) { return (aVal.getTime() - bVal.getTime()) * order; } if (typeof aVal === 'string' && typeof bVal === 'string') { return aVal.localeCompare(bVal) * order; } return 0; }); } // Pagination const offset = options?.offset ?? 0; const limit = options?.limit ?? uploads.length; return uploads.slice(offset, offset + limit); }, }; }; //# sourceMappingURL=functionContextFs.js.map