@imjp/writenex-astro 1.3.0 → 1.4.0
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/dist/{chunk-OWYFIQFK.js → chunk-4H63L4YO.js} +2 -2
- package/dist/{chunk-GUUSVFBP.js → chunk-EEUN46Q2.js} +5 -5
- package/dist/{chunk-TQAYIZOA.js → chunk-GYAFIVVI.js} +2 -2
- package/dist/chunk-GYAFIVVI.js.map +1 -0
- package/dist/chunk-HNS5YKP3.js +241 -0
- package/dist/chunk-HNS5YKP3.js.map +1 -0
- package/dist/{chunk-YBCPOLMY.js → chunk-P5KMSHFP.js} +2 -1
- package/dist/{chunk-YBCPOLMY.js.map → chunk-P5KMSHFP.js.map} +1 -1
- package/dist/{chunk-GIS7XEJF.js → chunk-XVQNYPOI.js} +66 -4
- package/dist/chunk-XVQNYPOI.js.map +1 -0
- package/dist/chunk-YRSIZLHE.js +1 -0
- package/dist/{chunk-S2OUQLMK.js → chunk-ZWUGHWHD.js} +85 -10
- package/dist/chunk-ZWUGHWHD.js.map +1 -0
- package/dist/client/index.css +1 -1
- package/dist/client/index.css.map +1 -1
- package/dist/client/index.js +114 -114
- package/dist/client/index.js.map +1 -1
- package/dist/config/index.d.ts +3 -2
- package/dist/config/index.js +11 -3
- package/dist/{config-CliL0CoN.d.ts → config-B7t8CjL1.d.ts} +38 -60
- package/dist/{content-TuL3GT66.d.ts → content-CwcgR8P6.d.ts} +1 -1
- package/dist/discovery/index.d.ts +2 -2
- package/dist/discovery/index.js +3 -3
- package/dist/fields/index.d.ts +16 -0
- package/dist/fields/index.js +13 -0
- package/dist/fields-DUSm13nm.d.ts +223 -0
- package/dist/filesystem/index.d.ts +2 -2
- package/dist/filesystem/index.js +3 -3
- package/dist/index.d.ts +5 -4
- package/dist/index.js +16 -8
- package/dist/index.js.map +1 -1
- package/dist/{loader-VGNXC2XJ.js → loader-B5WZCVBC.js} +3 -3
- package/dist/loader-B5WZCVBC.js.map +1 -0
- package/dist/{schema-DDJyoVkj.d.ts → schema-nLMfZ9-o.d.ts} +79 -53
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +5 -5
- package/package.json +5 -1
- package/src/client/components/FrontmatterForm/FrontmatterForm.css +104 -0
- package/src/client/components/FrontmatterForm/FrontmatterForm.tsx +452 -26
- package/src/config/defaults.ts +1 -0
- package/src/config/index.ts +3 -0
- package/src/config/loader.ts +18 -0
- package/src/config/schema.ts +114 -73
- package/src/discovery/schema.ts +120 -1
- package/src/fields/collection.ts +67 -0
- package/src/fields/fields.ts +150 -0
- package/src/fields/index.ts +42 -0
- package/src/fields/resolve.ts +149 -0
- package/src/fields/types.ts +179 -0
- package/src/filesystem/reader.ts +3 -1
- package/src/index.ts +3 -0
- package/src/integration.ts +4 -1
- package/src/types/config.ts +86 -63
- package/src/types/index.ts +3 -0
- package/dist/chunk-GIS7XEJF.js.map +0 -1
- package/dist/chunk-KIKIPIFA.js +0 -1
- package/dist/chunk-S2OUQLMK.js.map +0 -1
- package/dist/chunk-TQAYIZOA.js.map +0 -1
- package/dist/client/index.d.ts +0 -19
- /package/dist/{chunk-OWYFIQFK.js.map → chunk-4H63L4YO.js.map} +0 -0
- /package/dist/{chunk-GUUSVFBP.js.map → chunk-EEUN46Q2.js.map} +0 -0
- /package/dist/{chunk-KIKIPIFA.js.map → chunk-YRSIZLHE.js.map} +0 -0
- /package/dist/{loader-VGNXC2XJ.js.map → fields/index.js.map} +0 -0
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
isValidPattern,
|
|
5
5
|
readContentFile,
|
|
6
6
|
resolvePatternTokens
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GYAFIVVI.js";
|
|
8
8
|
|
|
9
9
|
// src/core/errors.ts
|
|
10
10
|
var WritenexErrorCode = /* @__PURE__ */ ((WritenexErrorCode2) => {
|
|
@@ -1407,4 +1407,4 @@ export {
|
|
|
1407
1407
|
updateContent,
|
|
1408
1408
|
deleteContent
|
|
1409
1409
|
};
|
|
1410
|
-
//# sourceMappingURL=chunk-
|
|
1410
|
+
//# sourceMappingURL=chunk-4H63L4YO.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
discoverCollections,
|
|
3
3
|
mergeCollections
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XVQNYPOI.js";
|
|
5
5
|
import {
|
|
6
6
|
ApiBadRequestError,
|
|
7
7
|
ApiMethodNotAllowedError,
|
|
@@ -27,12 +27,12 @@ import {
|
|
|
27
27
|
updateContent,
|
|
28
28
|
uploadImage,
|
|
29
29
|
wrapError
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-4H63L4YO.js";
|
|
31
31
|
import {
|
|
32
32
|
getCollectionSummaries,
|
|
33
33
|
getContentFilePath,
|
|
34
34
|
readContentFile
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-GYAFIVVI.js";
|
|
36
36
|
|
|
37
37
|
// src/server/cache.ts
|
|
38
38
|
var DEFAULT_TTL_MS = 5 * 60 * 1e3;
|
|
@@ -634,7 +634,7 @@ var handleGetConfig = async (_req, res, _params, context) => {
|
|
|
634
634
|
};
|
|
635
635
|
var handleGetConfigPath = async (_req, res, _params, context) => {
|
|
636
636
|
const { projectRoot } = context;
|
|
637
|
-
const { findConfigFile } = await import("./loader-
|
|
637
|
+
const { findConfigFile } = await import("./loader-B5WZCVBC.js");
|
|
638
638
|
const configPath = findConfigFile(projectRoot);
|
|
639
639
|
sendJson(res, {
|
|
640
640
|
configPath,
|
|
@@ -1328,4 +1328,4 @@ export {
|
|
|
1328
1328
|
sendError,
|
|
1329
1329
|
sendWritenexError
|
|
1330
1330
|
};
|
|
1331
|
-
//# sourceMappingURL=chunk-
|
|
1331
|
+
//# sourceMappingURL=chunk-EEUN46Q2.js.map
|
|
@@ -14,7 +14,7 @@ function extractSlug(filePath, collectionPath) {
|
|
|
14
14
|
const filename = basename(relativePath);
|
|
15
15
|
const ext = extname(filename);
|
|
16
16
|
if (filename === "index.md" || filename === "index.mdx") {
|
|
17
|
-
const parts = relativePath.split("/");
|
|
17
|
+
const parts = relativePath.replace(/\\/g, "/").split("/");
|
|
18
18
|
if (parts.length >= 2) {
|
|
19
19
|
const slug = parts[parts.length - 2];
|
|
20
20
|
if (slug) return slug;
|
|
@@ -571,4 +571,4 @@ export {
|
|
|
571
571
|
isValidPattern,
|
|
572
572
|
getSupportedTokens
|
|
573
573
|
};
|
|
574
|
-
//# sourceMappingURL=chunk-
|
|
574
|
+
//# sourceMappingURL=chunk-GYAFIVVI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/filesystem/reader.ts","../src/discovery/patterns.ts"],"sourcesContent":["/**\n * @fileoverview Filesystem reader for content collections\n *\n * This module provides functions for reading content files from the filesystem,\n * parsing frontmatter, and extracting content metadata.\n *\n * ## Features:\n * - Read individual content files with frontmatter parsing\n * - List all content files in a collection\n * - Generate content summaries for listing\n * - Support for .md and .mdx files\n *\n * @module @writenex/astro/filesystem/reader\n */\n\nimport { existsSync } from \"node:fs\";\nimport { readdir, readFile, stat } from \"node:fs/promises\";\nimport { basename, extname, join, relative } from \"node:path\";\nimport matter from \"gray-matter\";\nimport type { ContentItem, ContentSummary } from \"@/types\";\n\n/**\n * Supported content file extensions\n */\nconst CONTENT_EXTENSIONS = [\".md\", \".mdx\"];\n\n/**\n * Maximum excerpt length in characters\n */\nconst EXCERPT_LENGTH = 150;\n\n/**\n * Options for reading content\n */\nexport interface ReadContentOptions {\n /** Include draft content in listings */\n includeDrafts?: boolean;\n /** Sort field for listings */\n sortBy?: string;\n /** Sort order */\n sortOrder?: \"asc\" | \"desc\";\n}\n\n/**\n * Result of reading a content file\n */\nexport interface ReadFileResult {\n /** Whether the read was successful */\n success: boolean;\n /** The content item (if successful) */\n content?: ContentItem;\n /** Error message (if failed) */\n error?: string;\n}\n\n/**\n * Check if a file is a content file based on extension\n *\n * @param filename - The filename to check\n * @returns True if the file is a content file\n */\nexport function isContentFile(filename: string): boolean {\n const ext = extname(filename).toLowerCase();\n return CONTENT_EXTENSIONS.includes(ext);\n}\n\n/**\n * Extract slug from a content file path\n *\n * Handles various file patterns:\n * - `my-post.md` -> `my-post`\n * - `2024-01-15-my-post.md` -> `2024-01-15-my-post`\n * - `my-post/index.md` -> `my-post`\n *\n * @param filePath - Path to the content file\n * @param collectionPath - Path to the collection directory\n * @returns The extracted slug\n */\nexport function extractSlug(filePath: string, collectionPath: string): string {\n const relativePath = relative(collectionPath, filePath);\n const filename = basename(relativePath);\n const ext = extname(filename);\n\n // Handle index files (folder-based content)\n // On Windows, path.relative() uses backslashes — normalise to forward slashes\n // so the split works on both platforms.\n if (filename === \"index.md\" || filename === \"index.mdx\") {\n const parts = relativePath.replace(/\\\\/g, \"/\").split(\"/\");\n if (parts.length >= 2) {\n const slug = parts[parts.length - 2];\n if (slug) return slug;\n }\n }\n\n // Remove extension to get slug\n return filename.slice(0, -ext.length);\n}\n\n/**\n * Generate an excerpt from markdown content\n *\n * @param body - The markdown body content\n * @param maxLength - Maximum excerpt length\n * @returns The generated excerpt\n */\nexport function generateExcerpt(\n body: string,\n maxLength: number = EXCERPT_LENGTH\n): string {\n // Remove markdown formatting for cleaner excerpt\n const cleaned = body\n // Remove headers\n .replace(/^#{1,6}\\s+/gm, \"\")\n // Remove bold/italic\n .replace(/\\*\\*([^*]+)\\*\\*/g, \"$1\")\n .replace(/\\*([^*]+)\\*/g, \"$1\")\n .replace(/__([^_]+)__/g, \"$1\")\n .replace(/_([^_]+)_/g, \"$1\")\n // Remove links but keep text\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n // Remove images\n .replace(/!\\[([^\\]]*)\\]\\([^)]+\\)/g, \"\")\n // Remove code blocks\n .replace(/```[\\s\\S]*?```/g, \"\")\n .replace(/`([^`]+)`/g, \"$1\")\n // Remove blockquotes\n .replace(/^>\\s+/gm, \"\")\n // Remove horizontal rules\n .replace(/^[-*_]{3,}$/gm, \"\")\n // Collapse whitespace\n .replace(/\\s+/g, \" \")\n .trim();\n\n if (cleaned.length <= maxLength) {\n return cleaned;\n }\n\n // Truncate at word boundary\n const truncated = cleaned.slice(0, maxLength);\n const lastSpace = truncated.lastIndexOf(\" \");\n\n if (lastSpace > maxLength * 0.7) {\n return truncated.slice(0, lastSpace) + \"...\";\n }\n\n return truncated + \"...\";\n}\n\n/**\n * Read and parse a single content file\n *\n * @param filePath - Absolute path to the content file\n * @param collectionPath - Path to the collection directory\n * @returns ReadFileResult with the parsed content or error\n *\n * @example\n * ```typescript\n * const result = await readContentFile(\n * '/project/src/content/blog/my-post.md',\n * '/project/src/content/blog'\n * );\n *\n * if (result.success) {\n * console.log(result.content.frontmatter.title);\n * }\n * ```\n */\nexport async function readContentFile(\n filePath: string,\n collectionPath: string\n): Promise<ReadFileResult> {\n try {\n // Check if file exists\n if (!existsSync(filePath)) {\n return {\n success: false,\n error: `File not found: ${filePath}`,\n };\n }\n\n // Read file content and stats in parallel\n const [raw, stats] = await Promise.all([\n readFile(filePath, \"utf-8\"),\n stat(filePath),\n ]);\n\n // Parse frontmatter\n const { data: frontmatter, content: body } = matter(raw);\n\n // Extract slug\n const id = extractSlug(filePath, collectionPath);\n\n return {\n success: true,\n content: {\n id,\n path: filePath,\n frontmatter,\n body: body.trim(),\n raw,\n mtime: stats.mtimeMs,\n },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n success: false,\n error: `Failed to read content file: ${message}`,\n };\n }\n}\n\n/**\n * List all content files in a directory recursively\n *\n * @param dirPath - Path to the directory to scan\n * @returns Array of absolute file paths\n */\nasync function listFilesRecursive(dirPath: string): Promise<string[]> {\n const files: string[] = [];\n\n if (!existsSync(dirPath)) {\n return files;\n }\n\n const entries = await readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n // Recursively scan subdirectories\n const subFiles = await listFilesRecursive(fullPath);\n files.push(...subFiles);\n } else if (entry.isFile() && isContentFile(entry.name)) {\n files.push(fullPath);\n }\n }\n\n return files;\n}\n\n/**\n * Read all content files in a collection\n *\n * @param collectionPath - Absolute path to the collection directory\n * @param options - Read options\n * @returns Array of content items\n *\n * @example\n * ```typescript\n * const items = await readCollection('/project/src/content/blog', {\n * includeDrafts: false,\n * sortBy: 'pubDate',\n * sortOrder: 'desc',\n * });\n * ```\n */\nexport async function readCollection(\n collectionPath: string,\n options: ReadContentOptions = {}\n): Promise<ContentItem[]> {\n const { includeDrafts = true, sortBy, sortOrder = \"desc\" } = options;\n\n // Get all content files\n const filePaths = await listFilesRecursive(collectionPath);\n\n // Read and parse all files\n const results = await Promise.all(\n filePaths.map((fp) => readContentFile(fp, collectionPath))\n );\n\n // Filter successful reads and optionally filter drafts\n let items = results\n .filter(\n (r): r is { success: true; content: ContentItem } =>\n r.success && !!r.content\n )\n .map((r) => r.content)\n .filter((item) => {\n if (!includeDrafts && item.frontmatter.draft === true) {\n return false;\n }\n return true;\n });\n\n // Sort if requested\n if (sortBy) {\n items = items.sort((a, b) => {\n const aVal = a.frontmatter[sortBy];\n const bVal = b.frontmatter[sortBy];\n\n // Handle undefined values\n if (aVal === undefined && bVal === undefined) return 0;\n if (aVal === undefined) return sortOrder === \"asc\" ? -1 : 1;\n if (bVal === undefined) return sortOrder === \"asc\" ? 1 : -1;\n\n // Compare values (convert to string for comparison)\n const aStr = String(aVal);\n const bStr = String(bVal);\n if (aStr < bStr) return sortOrder === \"asc\" ? -1 : 1;\n if (aStr > bStr) return sortOrder === \"asc\" ? 1 : -1;\n return 0;\n });\n }\n\n return items;\n}\n\n/**\n * Convert a content item to a summary for listing\n *\n * @param item - The full content item\n * @returns Content summary with essential fields\n */\nexport function toContentSummary(item: ContentItem): ContentSummary {\n const { id, path, frontmatter, body } = item;\n\n // Support both pubDate and publishDate naming conventions\n const dateValue =\n frontmatter.pubDate ?? frontmatter.publishDate ?? frontmatter.date;\n\n return {\n id,\n path,\n title: String(frontmatter.title ?? id),\n pubDate: dateValue ? String(dateValue) : undefined,\n draft: frontmatter.draft === true,\n excerpt: generateExcerpt(body),\n };\n}\n\n/**\n * Get content summaries for a collection\n *\n * @param collectionPath - Absolute path to the collection directory\n * @param options - Read options\n * @returns Array of content summaries\n */\nexport async function getCollectionSummaries(\n collectionPath: string,\n options: ReadContentOptions = {}\n): Promise<ContentSummary[]> {\n const items = await readCollection(collectionPath, options);\n return items.map(toContentSummary);\n}\n\n/**\n * Get the count of content files in a collection\n *\n * @param collectionPath - Absolute path to the collection directory\n * @returns Number of content files\n */\nexport async function getCollectionCount(\n collectionPath: string\n): Promise<number> {\n const filePaths = await listFilesRecursive(collectionPath);\n return filePaths.length;\n}\n\n/**\n * Check if a collection directory exists and contains content\n *\n * @param collectionPath - Absolute path to the collection directory\n * @returns Object with exists and hasContent flags\n */\nexport async function checkCollection(collectionPath: string): Promise<{\n exists: boolean;\n hasContent: boolean;\n count: number;\n}> {\n if (!existsSync(collectionPath)) {\n return { exists: false, hasContent: false, count: 0 };\n }\n\n const count = await getCollectionCount(collectionPath);\n\n return {\n exists: true,\n hasContent: count > 0,\n count,\n };\n}\n\n/**\n * Get file stats for a content file\n *\n * @param filePath - Path to the content file\n * @returns File stats or null if file doesn't exist\n */\nexport async function getFileStats(filePath: string): Promise<{\n size: number;\n mtime: Date;\n ctime: Date;\n} | null> {\n try {\n const stats = await stat(filePath);\n return {\n size: stats.size,\n mtime: stats.mtime,\n ctime: stats.birthtime,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Get the file path for a content item by ID\n *\n * Searches for the content file in the collection directory,\n * handling different content structures:\n * - Folder-based: `slug/index.md` or `slug/index.mdx`\n * - Flat file: `slug.md` or `slug.mdx`\n *\n * @param collectionPath - Path to the collection directory\n * @param contentId - Content ID (slug)\n * @returns File path if found, null otherwise\n *\n * @example\n * ```typescript\n * const filePath = getContentFilePath('/project/src/content/blog', 'my-post');\n * // Returns: '/project/src/content/blog/my-post.md' or\n * // '/project/src/content/blog/my-post/index.md'\n * ```\n */\nexport function getContentFilePath(\n collectionPath: string,\n contentId: string\n): string | null {\n // Try folder-based structure first (slug/index.md or slug/index.mdx)\n const indexMdPath = join(collectionPath, contentId, \"index.md\");\n if (existsSync(indexMdPath)) {\n return indexMdPath;\n }\n\n const indexMdxPath = join(collectionPath, contentId, \"index.mdx\");\n if (existsSync(indexMdxPath)) {\n return indexMdxPath;\n }\n\n // Try flat file structure (slug.md or slug.mdx)\n const flatMdPath = join(collectionPath, `${contentId}.md`);\n if (existsSync(flatMdPath)) {\n return flatMdPath;\n }\n\n const flatMdxPath = join(collectionPath, `${contentId}.mdx`);\n if (existsSync(flatMdxPath)) {\n return flatMdxPath;\n }\n\n return null;\n}\n","/**\n * @fileoverview File pattern detection for content collections\n *\n * This module provides functions to detect and work with file naming patterns\n * in Astro content collections.\n *\n * ## Supported Patterns:\n * - `{slug}.md` - Simple slug-based naming\n * - `{date}-{slug}.md` - Date-prefixed naming (2024-01-15-my-post.md)\n * - `{year}/{slug}.md` - Year folder structure\n * - `{year}/{month}/{slug}.md` - Year/month folder structure\n * - `{year}/{month}/{day}/{slug}.md` - Full date folder structure\n * - `{slug}/index.md` - Folder-based with index file\n * - `{category}/{slug}.md` - Category folder structure\n * - `{category}/{slug}/index.md` - Category with folder-based content\n * - `{lang}/{slug}.md` - Language-prefixed content (i18n)\n * - `{lang}/{slug}/index.md` - Language with folder-based content\n *\n * ## Custom Patterns:\n * Developers can configure custom patterns in their collection config.\n * Custom tokens are resolved from frontmatter data or use default values.\n *\n * ## Detection Process:\n * 1. Scan collection directory for all content files\n * 2. Analyze file paths and names for common patterns\n * 3. Score each pattern based on match frequency\n * 4. Return the best matching pattern\n *\n * @module @writenex/astro/discovery/patterns\n */\n\nimport { existsSync } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { extname, join, relative } from \"node:path\";\nimport { isContentFile } from \"@/filesystem/reader\";\n\n/**\n * Pattern definition with regex and template\n */\ninterface PatternDefinition {\n /** Pattern name for identification */\n name: string;\n /** Template string with tokens */\n template: string;\n /** Regex to match against file paths */\n regex: RegExp;\n /** Function to extract tokens from a match */\n extract: (match: RegExpMatchArray, ext: string) => Record<string, string>;\n /** Priority when multiple patterns match (higher = preferred) */\n priority: number;\n}\n\n/**\n * Result of pattern detection\n */\nexport interface PatternDetectionResult {\n /** The detected pattern template */\n pattern: string;\n /** Confidence score (0-1) */\n confidence: number;\n /** Number of files that matched this pattern */\n matchCount: number;\n /** Total files analyzed */\n totalFiles: number;\n /** Sample matches for debugging */\n samples: Array<{\n filePath: string;\n extracted: Record<string, string>;\n }>;\n}\n\n/**\n * All supported pattern definitions\n *\n * Order matters - more specific patterns should come first.\n * Higher priority patterns are preferred when multiple patterns match.\n */\nconst PATTERN_DEFINITIONS: PatternDefinition[] = [\n // {year}/{month}/{day}/{slug}.md - Full date folder structure\n {\n name: \"year-month-day-slug\",\n template: \"{year}/{month}/{day}/{slug}.md\",\n regex: /^(\\d{4})\\/(\\d{2})\\/(\\d{2})\\/([^/]+)\\.(md|mdx)$/,\n extract: (match, ext) => ({\n year: match[1] ?? \"\",\n month: match[2] ?? \"\",\n day: match[3] ?? \"\",\n slug: match[4] ?? \"\",\n extension: ext,\n }),\n priority: 95,\n },\n\n // {year}/{month}/{slug}.md - Year/month nested date structure\n {\n name: \"year-month-slug\",\n template: \"{year}/{month}/{slug}.md\",\n regex: /^(\\d{4})\\/(\\d{2})\\/([^/]+)\\.(md|mdx)$/,\n extract: (match, ext) => ({\n year: match[1] ?? \"\",\n month: match[2] ?? \"\",\n slug: match[3] ?? \"\",\n extension: ext,\n }),\n priority: 90,\n },\n\n // {year}/{slug}.md - Year folder structure\n {\n name: \"year-slug\",\n template: \"{year}/{slug}.md\",\n regex: /^(\\d{4})\\/([^/]+)\\.(md|mdx)$/,\n extract: (match, ext) => ({\n year: match[1] ?? \"\",\n slug: match[2] ?? \"\",\n extension: ext,\n }),\n priority: 85,\n },\n\n // {lang}/{slug}/index.md - Language with folder-based content (i18n)\n {\n name: \"lang-folder-index\",\n template: \"{lang}/{slug}/index.md\",\n regex: /^([a-z]{2}(?:-[A-Z]{2})?)\\/([^/]+)\\/index\\.(md|mdx)$/,\n extract: (match, ext) => ({\n lang: match[1] ?? \"\",\n slug: match[2] ?? \"\",\n extension: ext,\n }),\n priority: 82,\n },\n\n // {category}/{slug}/index.md - Category with folder-based content\n {\n name: \"category-folder-index\",\n template: \"{category}/{slug}/index.md\",\n regex: /^([^/]+)\\/([^/]+)\\/index\\.(md|mdx)$/,\n extract: (match, ext) => ({\n category: match[1] ?? \"\",\n slug: match[2] ?? \"\",\n extension: ext,\n }),\n priority: 80,\n },\n\n // {slug}/index.md - Folder-based content\n {\n name: \"folder-index\",\n template: \"{slug}/index.md\",\n regex: /^([^/]+)\\/index\\.(md|mdx)$/,\n extract: (match, ext) => ({\n slug: match[1] ?? \"\",\n extension: ext,\n }),\n priority: 75,\n },\n\n // {date}-{slug}.md - Date-prefixed (ISO format)\n {\n name: \"date-slug\",\n template: \"{date}-{slug}.md\",\n regex: /^(\\d{4}-\\d{2}-\\d{2})-(.+)\\.(md|mdx)$/,\n extract: (match, ext) => ({\n date: match[1] ?? \"\",\n slug: match[2] ?? \"\",\n extension: ext,\n }),\n priority: 70,\n },\n\n // {lang}/{slug}.md - Language-prefixed content (i18n)\n // Matches: en/my-post.md, pt-BR/my-post.md\n {\n name: \"lang-slug\",\n template: \"{lang}/{slug}.md\",\n regex: /^([a-z]{2}(?:-[A-Z]{2})?)\\/([^/]+)\\.(md|mdx)$/,\n extract: (match, ext) => ({\n lang: match[1] ?? \"\",\n slug: match[2] ?? \"\",\n extension: ext,\n }),\n priority: 60,\n },\n\n // {category}/{slug}.md - Category folder (catch-all for non-date/non-lang folders)\n {\n name: \"category-slug\",\n template: \"{category}/{slug}.md\",\n regex: /^([^/]+)\\/([^/]+)\\.(md|mdx)$/,\n extract: (match, ext) => ({\n category: match[1] ?? \"\",\n slug: match[2] ?? \"\",\n extension: ext,\n }),\n priority: 50,\n },\n\n // {slug}.md - Simple flat structure (default fallback)\n {\n name: \"simple-slug\",\n template: \"{slug}.md\",\n regex: /^([^/]+)\\.(md|mdx)$/,\n extract: (match, ext) => ({\n slug: match[1] ?? \"\",\n extension: ext,\n }),\n priority: 10,\n },\n];\n\n/**\n * List all content files in a directory recursively\n *\n * @param dirPath - Directory to scan\n * @returns Array of relative file paths\n */\nasync function listContentFiles(dirPath: string): Promise<string[]> {\n const files: string[] = [];\n\n if (!existsSync(dirPath)) {\n return files;\n }\n\n async function scan(currentPath: string, relativeTo: string): Promise<void> {\n const entries = await readdir(currentPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(currentPath, entry.name);\n const relativePath = relative(relativeTo, fullPath);\n\n if (entry.isDirectory()) {\n // Skip hidden and special directories\n if (!entry.name.startsWith(\".\") && !entry.name.startsWith(\"_\")) {\n await scan(fullPath, relativeTo);\n }\n } else if (entry.isFile() && isContentFile(entry.name)) {\n files.push(relativePath);\n }\n }\n }\n\n await scan(dirPath, dirPath);\n return files;\n}\n\n/**\n * Try to match a file path against all pattern definitions\n *\n * @param relativePath - Relative path to the content file\n * @returns Matched pattern and extracted tokens, or null\n */\nfunction matchPattern(\n relativePath: string\n): { pattern: PatternDefinition; match: RegExpMatchArray } | null {\n // Normalize path separators\n const normalizedPath = relativePath.replace(/\\\\/g, \"/\");\n\n for (const pattern of PATTERN_DEFINITIONS) {\n const match = normalizedPath.match(pattern.regex);\n if (match) {\n return { pattern, match };\n }\n }\n\n return null;\n}\n\n/**\n * Detect the file naming pattern used in a collection\n *\n * Analyzes all content files in the collection directory and determines\n * the most likely pattern based on file names and structure.\n *\n * @param collectionPath - Absolute path to the collection directory\n * @returns Pattern detection result with confidence score\n *\n * @example\n * ```typescript\n * const result = await detectFilePattern('/project/src/content/blog');\n * console.log(result.pattern); // \"{date}-{slug}.md\"\n * console.log(result.confidence); // 0.95\n * ```\n */\nexport async function detectFilePattern(\n collectionPath: string\n): Promise<PatternDetectionResult> {\n const files = await listContentFiles(collectionPath);\n\n if (files.length === 0) {\n return {\n pattern: \"{slug}.md\",\n confidence: 0,\n matchCount: 0,\n totalFiles: 0,\n samples: [],\n };\n }\n\n // Count matches for each pattern\n const patternCounts = new Map<\n string,\n {\n pattern: PatternDefinition;\n count: number;\n samples: Array<{ filePath: string; extracted: Record<string, string> }>;\n extension: string;\n }\n >();\n\n for (const pattern of PATTERN_DEFINITIONS) {\n patternCounts.set(pattern.name, {\n pattern,\n count: 0,\n samples: [],\n extension: \".md\",\n });\n }\n\n // Analyze each file\n for (const filePath of files) {\n const result = matchPattern(filePath);\n\n if (result) {\n const { pattern, match } = result;\n const entry = patternCounts.get(pattern.name);\n\n if (entry) {\n const ext = extname(filePath);\n const extracted = pattern.extract(match, ext);\n\n entry.count++;\n entry.extension = ext;\n\n // Keep up to 3 samples\n if (entry.samples.length < 3) {\n entry.samples.push({ filePath, extracted });\n }\n }\n }\n }\n\n // Find the best matching pattern\n // Consider both match count and pattern priority\n let bestPattern: PatternDetectionResult | null = null;\n let bestScore = -1;\n\n for (const [, entry] of patternCounts) {\n if (entry.count === 0) continue;\n\n // Score = (match ratio * 100) + priority\n // This ensures high match ratio wins, but priority breaks ties\n const matchRatio = entry.count / files.length;\n const score = matchRatio * 100 + entry.pattern.priority;\n\n if (score > bestScore) {\n bestScore = score;\n\n // Adjust template for actual extension used\n let template = entry.pattern.template;\n if (entry.extension === \".mdx\") {\n template = template.replace(\".md\", \".mdx\");\n }\n\n bestPattern = {\n pattern: template,\n confidence: matchRatio,\n matchCount: entry.count,\n totalFiles: files.length,\n samples: entry.samples,\n };\n }\n }\n\n // Return best pattern or default\n return (\n bestPattern ?? {\n pattern: \"{slug}.md\",\n confidence: 0,\n matchCount: 0,\n totalFiles: files.length,\n samples: [],\n }\n );\n}\n\n/**\n * Generate a file path from a pattern and tokens\n *\n * @param pattern - Pattern template (e.g., \"{date}-{slug}.md\")\n * @param tokens - Token values to substitute\n * @returns Generated file path\n *\n * @example\n * ```typescript\n * const path = generatePathFromPattern(\n * \"{date}-{slug}.md\",\n * { date: \"2024-01-15\", slug: \"my-post\" }\n * );\n * // Returns: \"2024-01-15-my-post.md\"\n * ```\n */\nexport function generatePathFromPattern(\n pattern: string,\n tokens: Record<string, string>\n): string {\n let result = pattern;\n\n for (const [key, value] of Object.entries(tokens)) {\n result = result.replace(`{${key}}`, value);\n }\n\n return result;\n}\n\n/**\n * Parse a pattern template to extract token names\n *\n * @param pattern - Pattern template\n * @returns Array of token names\n *\n * @example\n * ```typescript\n * const tokens = parsePatternTokens(\"{year}/{month}/{slug}.md\");\n * // Returns: [\"year\", \"month\", \"slug\"]\n * ```\n */\nexport function parsePatternTokens(pattern: string): string[] {\n const tokenRegex = /\\{([^}]+)\\}/g;\n const tokens: string[] = [];\n let match;\n\n while ((match = tokenRegex.exec(pattern)) !== null) {\n if (match[1]) {\n tokens.push(match[1]);\n }\n }\n\n return tokens;\n}\n\n/**\n * Validate that a pattern has all required tokens\n *\n * @param pattern - Pattern template\n * @param requiredTokens - Required token names\n * @returns True if all required tokens are present\n */\nexport function validatePattern(\n pattern: string,\n requiredTokens: string[] = [\"slug\"]\n): boolean {\n const tokens = parsePatternTokens(pattern);\n return requiredTokens.every((req) => tokens.includes(req));\n}\n\n/**\n * Get the default extension for a pattern\n *\n * @param pattern - Pattern template\n * @returns The file extension (.md or .mdx)\n */\nexport function getPatternExtension(pattern: string): string {\n if (pattern.endsWith(\".mdx\")) {\n return \".mdx\";\n }\n return \".md\";\n}\n\n/**\n * Known token types and their default value generators\n */\ntype TokenResolver = (\n frontmatter: Record<string, unknown>,\n slug: string\n) => string;\n\nconst TOKEN_RESOLVERS: Record<string, TokenResolver> = {\n // Core tokens\n slug: (_fm, slug) => slug,\n\n // Date tokens - from pubDate or current date\n date: (fm) => {\n const pubDate = resolveDateFromFrontmatter(fm);\n return pubDate.toISOString().split(\"T\")[0] ?? \"\";\n },\n year: (fm) => {\n const pubDate = resolveDateFromFrontmatter(fm);\n return pubDate.getFullYear().toString();\n },\n month: (fm) => {\n const pubDate = resolveDateFromFrontmatter(fm);\n return (pubDate.getMonth() + 1).toString().padStart(2, \"0\");\n },\n day: (fm) => {\n const pubDate = resolveDateFromFrontmatter(fm);\n return pubDate.getDate().toString().padStart(2, \"0\");\n },\n\n // i18n tokens\n lang: (fm) => {\n if (typeof fm.lang === \"string\") return fm.lang;\n if (typeof fm.language === \"string\") return fm.language;\n if (typeof fm.locale === \"string\") return fm.locale;\n return \"en\"; // Default to English\n },\n\n // Organization tokens\n category: (fm) => {\n if (typeof fm.category === \"string\") return fm.category;\n if (Array.isArray(fm.categories) && typeof fm.categories[0] === \"string\") {\n return fm.categories[0];\n }\n return \"uncategorized\";\n },\n author: (fm) => {\n if (typeof fm.author === \"string\") return slugifyValue(fm.author);\n if (\n typeof fm.author === \"object\" &&\n fm.author !== null &&\n \"name\" in fm.author\n ) {\n return slugifyValue(String(fm.author.name));\n }\n return \"anonymous\";\n },\n type: (fm) => {\n if (typeof fm.type === \"string\") return fm.type;\n if (typeof fm.contentType === \"string\") return fm.contentType;\n return \"post\";\n },\n status: (fm) => {\n if (typeof fm.status === \"string\") return fm.status;\n if (fm.draft === true) return \"draft\";\n return \"published\";\n },\n series: (fm) => {\n if (typeof fm.series === \"string\") return slugifyValue(fm.series);\n return \"\";\n },\n collection: (fm) => {\n if (typeof fm.collection === \"string\") return fm.collection;\n return \"\";\n },\n};\n\n/**\n * Resolve a date from frontmatter\n *\n * Checks common date field names: pubDate, date, publishDate, createdAt\n *\n * @param frontmatter - Frontmatter data\n * @returns Resolved Date object\n */\nfunction resolveDateFromFrontmatter(\n frontmatter: Record<string, unknown>\n): Date {\n const dateFields = [\"pubDate\", \"date\", \"publishDate\", \"createdAt\", \"created\"];\n\n for (const field of dateFields) {\n const value = frontmatter[field];\n if (value instanceof Date) return value;\n if (typeof value === \"string\") {\n const parsed = new Date(value);\n if (!isNaN(parsed.getTime())) return parsed;\n }\n }\n\n return new Date();\n}\n\n/**\n * Convert a string to a URL-safe slug\n *\n * @param value - String to slugify\n * @returns URL-safe slug\n */\nfunction slugifyValue(value: string): string {\n return value\n .toLowerCase()\n .trim()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/[\\s_-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n}\n\n/**\n * Options for resolving pattern tokens\n */\nexport interface ResolveTokensOptions {\n /** The content slug */\n slug: string;\n /** Frontmatter data for resolving dynamic tokens */\n frontmatter?: Record<string, unknown>;\n /** Custom token values (override automatic resolution) */\n customTokens?: Record<string, string>;\n}\n\n/**\n * Resolve all tokens in a pattern to their values\n *\n * Token resolution priority:\n * 1. Custom tokens (explicitly provided)\n * 2. Known token resolvers (date, year, month, etc.)\n * 3. Frontmatter values (for custom tokens)\n * 4. Empty string (fallback)\n *\n * @param pattern - Pattern template with tokens\n * @param options - Resolution options\n * @returns Record of token names to resolved values\n *\n * @example\n * ```typescript\n * const tokens = resolvePatternTokens(\"{year}/{month}/{slug}.md\", {\n * slug: \"my-post\",\n * frontmatter: { pubDate: new Date(\"2024-06-15\") }\n * });\n * // Returns: { year: \"2024\", month: \"06\", slug: \"my-post\" }\n * ```\n */\nexport function resolvePatternTokens(\n pattern: string,\n options: ResolveTokensOptions\n): Record<string, string> {\n const { slug, frontmatter = {}, customTokens = {} } = options;\n const tokenNames = parsePatternTokens(pattern);\n const resolved: Record<string, string> = {};\n\n for (const tokenName of tokenNames) {\n // Priority 1: Custom tokens\n if (tokenName in customTokens) {\n resolved[tokenName] = customTokens[tokenName] ?? \"\";\n continue;\n }\n\n // Priority 2: Known token resolvers\n const resolver = TOKEN_RESOLVERS[tokenName];\n if (resolver) {\n resolved[tokenName] = resolver(frontmatter, slug);\n continue;\n }\n\n // Priority 3: Direct frontmatter value\n const fmValue = frontmatter[tokenName];\n if (typeof fmValue === \"string\") {\n resolved[tokenName] = slugifyValue(fmValue);\n continue;\n }\n if (typeof fmValue === \"number\") {\n resolved[tokenName] = fmValue.toString();\n continue;\n }\n\n // Priority 4: Fallback to empty string\n resolved[tokenName] = \"\";\n }\n\n return resolved;\n}\n\n/**\n * Check if a pattern is valid for content creation\n *\n * A pattern is valid if:\n * - It contains the {slug} token (required)\n * - It ends with .md or .mdx\n * - All tokens can be resolved\n *\n * @param pattern - Pattern template to validate\n * @returns Validation result with error message if invalid\n */\nexport function isValidPattern(pattern: string): {\n valid: boolean;\n error?: string;\n} {\n // Must contain slug token\n if (!pattern.includes(\"{slug}\")) {\n return { valid: false, error: \"Pattern must contain {slug} token\" };\n }\n\n // Must end with .md or .mdx\n if (!pattern.endsWith(\".md\") && !pattern.endsWith(\".mdx\")) {\n return { valid: false, error: \"Pattern must end with .md or .mdx\" };\n }\n\n // Check for unclosed tokens\n const unclosed = pattern.match(/\\{[^}]*$/);\n if (unclosed) {\n return { valid: false, error: \"Pattern contains unclosed token\" };\n }\n\n return { valid: true };\n}\n\n/**\n * Get list of all supported token names\n *\n * @returns Array of supported token names\n */\nexport function getSupportedTokens(): string[] {\n return Object.keys(TOKEN_RESOLVERS);\n}\n"],"mappings":"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","names":["existsSync","readdir","extname","join","relative","existsSync","readdir","join","relative","extname"]}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// src/fields/resolve.ts
|
|
2
|
+
var FIELD_KIND_TO_TYPE = {
|
|
3
|
+
text: "string",
|
|
4
|
+
slug: "string",
|
|
5
|
+
url: "string",
|
|
6
|
+
number: "number",
|
|
7
|
+
integer: "number",
|
|
8
|
+
select: "string",
|
|
9
|
+
multiselect: "array",
|
|
10
|
+
checkbox: "boolean",
|
|
11
|
+
date: "date",
|
|
12
|
+
datetime: "date",
|
|
13
|
+
image: "image",
|
|
14
|
+
file: "file",
|
|
15
|
+
object: "object",
|
|
16
|
+
array: "array",
|
|
17
|
+
blocks: "blocks",
|
|
18
|
+
relationship: "relationship",
|
|
19
|
+
"path-reference": "string",
|
|
20
|
+
markdoc: "markdoc",
|
|
21
|
+
mdx: "mdx",
|
|
22
|
+
conditional: "object",
|
|
23
|
+
child: "child",
|
|
24
|
+
"cloud-image": "image",
|
|
25
|
+
empty: "empty",
|
|
26
|
+
"empty-content": "empty-content",
|
|
27
|
+
"empty-document": "empty-document",
|
|
28
|
+
ignored: "ignored"
|
|
29
|
+
};
|
|
30
|
+
function resolveFieldDefinition(field) {
|
|
31
|
+
const type = FIELD_KIND_TO_TYPE[field.fieldKind] ?? "string";
|
|
32
|
+
const base = {
|
|
33
|
+
type,
|
|
34
|
+
required: field.validation?.isRequired ?? false,
|
|
35
|
+
label: field.label,
|
|
36
|
+
description: field.description,
|
|
37
|
+
default: field.defaultValue
|
|
38
|
+
};
|
|
39
|
+
switch (field.fieldKind) {
|
|
40
|
+
case "select":
|
|
41
|
+
return {
|
|
42
|
+
...base,
|
|
43
|
+
options: field.options
|
|
44
|
+
};
|
|
45
|
+
case "multiselect":
|
|
46
|
+
return {
|
|
47
|
+
...base,
|
|
48
|
+
items: "string"
|
|
49
|
+
};
|
|
50
|
+
case "image":
|
|
51
|
+
return {
|
|
52
|
+
...base,
|
|
53
|
+
directory: field.directory,
|
|
54
|
+
publicPath: field.publicPath
|
|
55
|
+
};
|
|
56
|
+
case "file":
|
|
57
|
+
return {
|
|
58
|
+
...base,
|
|
59
|
+
directory: field.directory,
|
|
60
|
+
publicPath: field.publicPath
|
|
61
|
+
};
|
|
62
|
+
case "object":
|
|
63
|
+
return {
|
|
64
|
+
...base,
|
|
65
|
+
fields: resolveObjectFields(field.fields)
|
|
66
|
+
};
|
|
67
|
+
case "array":
|
|
68
|
+
return {
|
|
69
|
+
...base,
|
|
70
|
+
itemField: resolveFieldDefinition(field.itemField),
|
|
71
|
+
itemLabel: field.itemLabel
|
|
72
|
+
};
|
|
73
|
+
case "blocks":
|
|
74
|
+
return {
|
|
75
|
+
...base,
|
|
76
|
+
blockTypes: resolveBlockTypes(field.blockTypes),
|
|
77
|
+
itemLabel: field.itemLabel
|
|
78
|
+
};
|
|
79
|
+
case "relationship":
|
|
80
|
+
return {
|
|
81
|
+
...base,
|
|
82
|
+
collection: field.collection
|
|
83
|
+
};
|
|
84
|
+
case "conditional":
|
|
85
|
+
return {
|
|
86
|
+
...base,
|
|
87
|
+
matchField: field.matchField,
|
|
88
|
+
matchValue: field.matchValue,
|
|
89
|
+
showField: resolveFieldDefinition(field.showField)
|
|
90
|
+
};
|
|
91
|
+
case "text":
|
|
92
|
+
return {
|
|
93
|
+
...base,
|
|
94
|
+
multiline: field.multiline
|
|
95
|
+
};
|
|
96
|
+
case "date":
|
|
97
|
+
case "datetime":
|
|
98
|
+
return {
|
|
99
|
+
...base,
|
|
100
|
+
format: field.fieldKind === "datetime" ? "datetime-local" : "date"
|
|
101
|
+
};
|
|
102
|
+
default:
|
|
103
|
+
return base;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function resolveObjectFields(fields2) {
|
|
107
|
+
const result = {};
|
|
108
|
+
for (const [key, value] of Object.entries(fields2)) {
|
|
109
|
+
result[key] = resolveFieldDefinition(value);
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
function resolveBlockTypes(blockTypes) {
|
|
114
|
+
const result = {};
|
|
115
|
+
for (const [key, value] of Object.entries(blockTypes)) {
|
|
116
|
+
result[key] = resolveFieldDefinition(value);
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/fields/collection.ts
|
|
122
|
+
function collection(config) {
|
|
123
|
+
const schema = {};
|
|
124
|
+
for (const [key, fieldDef] of Object.entries(config.schema)) {
|
|
125
|
+
schema[key] = resolveFieldDefinition(fieldDef);
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
name: config.name,
|
|
129
|
+
path: config.path,
|
|
130
|
+
filePattern: config.filePattern,
|
|
131
|
+
previewUrl: config.previewUrl,
|
|
132
|
+
schema,
|
|
133
|
+
images: config.images
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function singleton(config) {
|
|
137
|
+
const schema = {};
|
|
138
|
+
for (const [key, fieldDef] of Object.entries(config.schema)) {
|
|
139
|
+
schema[key] = resolveFieldDefinition(fieldDef);
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
name: config.name,
|
|
143
|
+
path: config.path,
|
|
144
|
+
previewUrl: config.previewUrl,
|
|
145
|
+
schema,
|
|
146
|
+
images: config.images
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/fields/fields.ts
|
|
151
|
+
function createField(fieldKind, config) {
|
|
152
|
+
return { fieldKind, ...config };
|
|
153
|
+
}
|
|
154
|
+
var fields = {
|
|
155
|
+
text(config = {}) {
|
|
156
|
+
return createField("text", config);
|
|
157
|
+
},
|
|
158
|
+
slug(config = {}) {
|
|
159
|
+
return createField("slug", config);
|
|
160
|
+
},
|
|
161
|
+
url(config = {}) {
|
|
162
|
+
return createField("url", config);
|
|
163
|
+
},
|
|
164
|
+
number(config = {}) {
|
|
165
|
+
return createField("number", config);
|
|
166
|
+
},
|
|
167
|
+
integer(config = {}) {
|
|
168
|
+
return createField("integer", config);
|
|
169
|
+
},
|
|
170
|
+
select(config) {
|
|
171
|
+
return createField("select", config);
|
|
172
|
+
},
|
|
173
|
+
multiselect(config) {
|
|
174
|
+
return createField("multiselect", config);
|
|
175
|
+
},
|
|
176
|
+
checkbox(config = {}) {
|
|
177
|
+
return createField("checkbox", config);
|
|
178
|
+
},
|
|
179
|
+
date(config = {}) {
|
|
180
|
+
return createField("date", config);
|
|
181
|
+
},
|
|
182
|
+
datetime(config = {}) {
|
|
183
|
+
return createField("datetime", config);
|
|
184
|
+
},
|
|
185
|
+
image(config = {}) {
|
|
186
|
+
return createField("image", config);
|
|
187
|
+
},
|
|
188
|
+
file(config = {}) {
|
|
189
|
+
return createField("file", config);
|
|
190
|
+
},
|
|
191
|
+
object(config) {
|
|
192
|
+
return createField("object", config);
|
|
193
|
+
},
|
|
194
|
+
array(config) {
|
|
195
|
+
return createField("array", config);
|
|
196
|
+
},
|
|
197
|
+
blocks(config) {
|
|
198
|
+
return createField("blocks", config);
|
|
199
|
+
},
|
|
200
|
+
relationship(config) {
|
|
201
|
+
return createField("relationship", config);
|
|
202
|
+
},
|
|
203
|
+
pathReference(config = {}) {
|
|
204
|
+
return createField("path-reference", config);
|
|
205
|
+
},
|
|
206
|
+
markdoc(config = {}) {
|
|
207
|
+
return createField("markdoc", config);
|
|
208
|
+
},
|
|
209
|
+
mdx(config = {}) {
|
|
210
|
+
return createField("mdx", config);
|
|
211
|
+
},
|
|
212
|
+
conditional(config) {
|
|
213
|
+
return createField("conditional", config);
|
|
214
|
+
},
|
|
215
|
+
child(config = {}) {
|
|
216
|
+
return createField("child", config);
|
|
217
|
+
},
|
|
218
|
+
cloudImage(config = {}) {
|
|
219
|
+
return createField("cloud-image", config);
|
|
220
|
+
},
|
|
221
|
+
empty(config = {}) {
|
|
222
|
+
return createField("empty", config);
|
|
223
|
+
},
|
|
224
|
+
emptyContent(config = {}) {
|
|
225
|
+
return createField("empty-content", config);
|
|
226
|
+
},
|
|
227
|
+
emptyDocument(config = {}) {
|
|
228
|
+
return createField("empty-document", config);
|
|
229
|
+
},
|
|
230
|
+
ignored(config = {}) {
|
|
231
|
+
return createField("ignored", config);
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export {
|
|
236
|
+
resolveFieldDefinition,
|
|
237
|
+
collection,
|
|
238
|
+
singleton,
|
|
239
|
+
fields
|
|
240
|
+
};
|
|
241
|
+
//# sourceMappingURL=chunk-HNS5YKP3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fields/resolve.ts","../src/fields/collection.ts","../src/fields/fields.ts"],"sourcesContent":["/**\n * @fileoverview FieldDefinition to SchemaField conversion\n *\n * This module converts FieldDefinition objects (from the builder API)\n * to the internal SchemaField format used by the form system.\n *\n * @module @writenex/astro/fields/resolve\n */\n\nimport type { SchemaField } from \"@/types\";\nimport type { FieldDefinition } from \"./types\";\n\nconst FIELD_KIND_TO_TYPE: Record<string, string> = {\n text: \"string\",\n slug: \"string\",\n url: \"string\",\n number: \"number\",\n integer: \"number\",\n select: \"string\",\n multiselect: \"array\",\n checkbox: \"boolean\",\n date: \"date\",\n datetime: \"date\",\n image: \"image\",\n file: \"file\",\n object: \"object\",\n array: \"array\",\n blocks: \"blocks\",\n relationship: \"relationship\",\n \"path-reference\": \"string\",\n markdoc: \"markdoc\",\n mdx: \"mdx\",\n conditional: \"object\",\n child: \"child\",\n \"cloud-image\": \"image\",\n empty: \"empty\",\n \"empty-content\": \"empty-content\",\n \"empty-document\": \"empty-document\",\n ignored: \"ignored\",\n};\n\nexport function resolveFieldDefinition(field: FieldDefinition): SchemaField {\n const type = FIELD_KIND_TO_TYPE[field.fieldKind] ?? \"string\";\n const base: SchemaField = {\n type: type as SchemaField[\"type\"],\n required: field.validation?.isRequired ?? false,\n label: field.label,\n description: field.description,\n default: field.defaultValue,\n };\n\n switch (field.fieldKind) {\n case \"select\":\n return {\n ...base,\n options: field.options,\n };\n\n case \"multiselect\":\n return {\n ...base,\n items: \"string\",\n };\n\n case \"image\":\n return {\n ...base,\n directory: field.directory,\n publicPath: field.publicPath,\n };\n\n case \"file\":\n return {\n ...base,\n directory: field.directory,\n publicPath: field.publicPath,\n };\n\n case \"object\":\n return {\n ...base,\n fields: resolveObjectFields(field.fields),\n };\n\n case \"array\":\n return {\n ...base,\n itemField: resolveFieldDefinition(field.itemField),\n itemLabel: field.itemLabel,\n };\n\n case \"blocks\":\n return {\n ...base,\n blockTypes: resolveBlockTypes(field.blockTypes),\n itemLabel: field.itemLabel,\n };\n\n case \"relationship\":\n return {\n ...base,\n collection: field.collection,\n };\n\n case \"conditional\":\n return {\n ...base,\n matchField: field.matchField,\n matchValue: field.matchValue,\n showField: resolveFieldDefinition(field.showField),\n };\n\n case \"text\":\n return {\n ...base,\n multiline: field.multiline,\n };\n\n case \"date\":\n case \"datetime\":\n return {\n ...base,\n format: field.fieldKind === \"datetime\" ? \"datetime-local\" : \"date\",\n };\n\n default:\n return base;\n }\n}\n\nfunction resolveObjectFields(\n fields: Record<string, FieldDefinition>\n): Record<string, SchemaField> {\n const result: Record<string, SchemaField> = {};\n for (const [key, value] of Object.entries(fields)) {\n result[key] = resolveFieldDefinition(value);\n }\n return result;\n}\n\nfunction resolveBlockTypes(\n blockTypes: Record<string, FieldDefinition>\n): Record<string, SchemaField> {\n const result: Record<string, SchemaField> = {};\n for (const [key, value] of Object.entries(blockTypes)) {\n result[key] = resolveFieldDefinition(value);\n }\n return result;\n}\n","/**\n * @fileoverview Collection and Singleton schema helpers\n *\n * These helpers provide a typed way to define collection and singleton\n * schemas using the fields builder API. They convert FieldDefinition\n * objects to internal SchemaField format.\n *\n * @module @writenex/astro/fields/collection\n */\n\nimport type { CollectionConfig, ImageConfig } from \"@/types\";\nimport { resolveFieldDefinition } from \"./resolve\";\nimport type { FieldDefinition } from \"./types\";\n\nexport interface CollectionSchemaConfig {\n name: string;\n path: string;\n filePattern?: string;\n previewUrl?: string;\n schema: Record<string, FieldDefinition>;\n images?: ImageConfig;\n}\n\nexport interface SingletonSchemaConfig {\n name: string;\n path: string;\n previewUrl?: string;\n schema: Record<string, FieldDefinition>;\n images?: ImageConfig;\n}\n\nexport function collection(config: CollectionSchemaConfig): CollectionConfig {\n const schema: Record<string, import(\"@/types\").SchemaField> = {};\n for (const [key, fieldDef] of Object.entries(config.schema)) {\n schema[key] = resolveFieldDefinition(fieldDef);\n }\n\n return {\n name: config.name,\n path: config.path,\n filePattern: config.filePattern,\n previewUrl: config.previewUrl,\n schema,\n images: config.images,\n };\n}\n\nexport function singleton(config: SingletonSchemaConfig): {\n name: string;\n path: string;\n previewUrl?: string;\n schema: Record<string, import(\"@/types\").SchemaField>;\n images?: ImageConfig;\n} {\n const schema: Record<string, import(\"@/types\").SchemaField> = {};\n for (const [key, fieldDef] of Object.entries(config.schema)) {\n schema[key] = resolveFieldDefinition(fieldDef);\n }\n\n return {\n name: config.name,\n path: config.path,\n previewUrl: config.previewUrl,\n schema,\n images: config.images,\n };\n}\n","/**\n * @fileoverview Fields builder API for @writenex/astro\n *\n * This module provides a TypeScript-first builder pattern for defining\n * content schema fields. Each field type is a method on the `fields` object.\n *\n * @module @writenex/astro/fields/fields\n */\n\nimport type {\n ArrayFieldConfig,\n BaseFieldConfig,\n BlocksFieldConfig,\n CheckboxFieldConfig,\n ChildFieldConfig,\n CloudImageFieldConfig,\n ConditionalFieldConfig,\n DateFieldConfig,\n DatetimeFieldConfig,\n FieldDefinition,\n FileFieldConfig,\n ImageFieldConfig,\n IntegerFieldConfig,\n MarkdocFieldConfig,\n MdxFieldConfig,\n MultiselectFieldConfig,\n NumberFieldConfig,\n ObjectFieldConfig,\n PathReferenceFieldConfig,\n RelationshipFieldConfig,\n SelectFieldConfig,\n SlugFieldConfig,\n TextFieldConfig,\n UrlFieldConfig,\n} from \"./types\";\n\nfunction createField<K extends FieldDefinition[\"fieldKind\"]>(\n fieldKind: K,\n config: Omit<Extract<FieldDefinition, { fieldKind: K }>, \"fieldKind\">\n): Extract<FieldDefinition, { fieldKind: K }> {\n return { fieldKind, ...config } as Extract<FieldDefinition, { fieldKind: K }>;\n}\n\nexport const fields = {\n text(config: TextFieldConfig = {}): FieldDefinition {\n return createField(\"text\", config);\n },\n\n slug(config: SlugFieldConfig = {}): FieldDefinition {\n return createField(\"slug\", config);\n },\n\n url(config: UrlFieldConfig = {}): FieldDefinition {\n return createField(\"url\", config);\n },\n\n number(config: NumberFieldConfig = {}): FieldDefinition {\n return createField(\"number\", config);\n },\n\n integer(config: IntegerFieldConfig = {}): FieldDefinition {\n return createField(\"integer\", config);\n },\n\n select(config: SelectFieldConfig): FieldDefinition {\n return createField(\"select\", config);\n },\n\n multiselect(config: MultiselectFieldConfig): FieldDefinition {\n return createField(\"multiselect\", config);\n },\n\n checkbox(config: CheckboxFieldConfig = {}): FieldDefinition {\n return createField(\"checkbox\", config);\n },\n\n date(config: DateFieldConfig = {}): FieldDefinition {\n return createField(\"date\", config);\n },\n\n datetime(config: DatetimeFieldConfig = {}): FieldDefinition {\n return createField(\"datetime\", config);\n },\n\n image(config: ImageFieldConfig = {}): FieldDefinition {\n return createField(\"image\", config);\n },\n\n file(config: FileFieldConfig = {}): FieldDefinition {\n return createField(\"file\", config);\n },\n\n object(config: ObjectFieldConfig): FieldDefinition {\n return createField(\"object\", config);\n },\n\n array(config: ArrayFieldConfig): FieldDefinition {\n return createField(\"array\", config);\n },\n\n blocks(config: BlocksFieldConfig): FieldDefinition {\n return createField(\"blocks\", config);\n },\n\n relationship(config: RelationshipFieldConfig): FieldDefinition {\n return createField(\"relationship\", config);\n },\n\n pathReference(config: PathReferenceFieldConfig = {}): FieldDefinition {\n return createField(\"path-reference\", config);\n },\n\n markdoc(config: MarkdocFieldConfig = {}): FieldDefinition {\n return createField(\"markdoc\", config);\n },\n\n mdx(config: MdxFieldConfig = {}): FieldDefinition {\n return createField(\"mdx\", config);\n },\n\n conditional(config: ConditionalFieldConfig): FieldDefinition {\n return createField(\"conditional\", config);\n },\n\n child(config: ChildFieldConfig = {}): FieldDefinition {\n return createField(\"child\", config);\n },\n\n cloudImage(config: CloudImageFieldConfig = {}): FieldDefinition {\n return createField(\"cloud-image\", config);\n },\n\n empty(config: BaseFieldConfig = {}): FieldDefinition {\n return createField(\"empty\", config);\n },\n\n emptyContent(config: BaseFieldConfig = {}): FieldDefinition {\n return createField(\"empty-content\", config);\n },\n\n emptyDocument(config: BaseFieldConfig = {}): FieldDefinition {\n return createField(\"empty-document\", config);\n },\n\n ignored(config: BaseFieldConfig = {}): FieldDefinition {\n return createField(\"ignored\", config);\n },\n};\n\nexport type Fields = typeof fields;\n"],"mappings":"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","names":["fields"]}
|
|
@@ -32,6 +32,7 @@ function applyCollectionDefaults(collection) {
|
|
|
32
32
|
function applyConfigDefaults(config = {}) {
|
|
33
33
|
return {
|
|
34
34
|
collections: (config.collections ?? []).map(applyCollectionDefaults),
|
|
35
|
+
singletons: (config.singletons ?? []).map(applyCollectionDefaults),
|
|
35
36
|
images: config.images ? { ...DEFAULT_IMAGE_CONFIG, ...config.images } : DEFAULT_IMAGE_CONFIG,
|
|
36
37
|
editor: config.editor ? { ...DEFAULT_EDITOR_CONFIG, ...config.editor } : DEFAULT_EDITOR_CONFIG,
|
|
37
38
|
discovery: config.discovery ? { ...DEFAULT_DISCOVERY_CONFIG, ...config.discovery } : DEFAULT_DISCOVERY_CONFIG,
|
|
@@ -49,4 +50,4 @@ export {
|
|
|
49
50
|
applyCollectionDefaults,
|
|
50
51
|
applyConfigDefaults
|
|
51
52
|
};
|
|
52
|
-
//# sourceMappingURL=chunk-
|
|
53
|
+
//# sourceMappingURL=chunk-P5KMSHFP.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/defaults.ts"],"sourcesContent":["/**\n * @fileoverview Default configuration values for @writenex/astro\n *\n * This module provides default values for all configuration options.\n * These defaults are applied when loading configuration to ensure\n * all required values are present.\n *\n * @module @writenex/astro/config/defaults\n */\n\nimport type {\n CollectionConfig,\n DiscoveryConfig,\n EditorConfig,\n ImageConfig,\n VersionHistoryConfig,\n WritenexConfig,\n} from \"@/types\";\n\n/**\n * Default image configuration\n */\nexport const DEFAULT_IMAGE_CONFIG: Required<ImageConfig> = {\n strategy: \"colocated\",\n publicPath: \"/images\",\n storagePath: \"public/images\",\n};\n\n/**\n * Default editor configuration\n */\nexport const DEFAULT_EDITOR_CONFIG: Required<EditorConfig> = {\n autosave: true,\n autosaveInterval: 3000,\n};\n\n/**\n * Default discovery configuration\n */\nexport const DEFAULT_DISCOVERY_CONFIG: Required<DiscoveryConfig> = {\n enabled: true,\n ignore: [\"**/node_modules/**\", \"**/.git/**\", \"**/dist/**\"],\n};\n\n/**\n * Default version history configuration\n */\nexport const DEFAULT_VERSION_HISTORY_CONFIG: Required<VersionHistoryConfig> = {\n enabled: true,\n maxVersions: 20,\n storagePath: \".writenex/versions\",\n};\n\n/**\n * Default file pattern for content files\n */\nexport const DEFAULT_FILE_PATTERN = \"{slug}.md\";\n\n/**\n * Default content directory path\n */\nexport const DEFAULT_CONTENT_PATH = \"src/content\";\n\n/**\n * Apply defaults to a collection configuration\n *\n * @param collection - Partial collection configuration\n * @returns Collection configuration with defaults applied\n */\nexport function applyCollectionDefaults(\n collection: CollectionConfig\n): Required<CollectionConfig> {\n return {\n name: collection.name,\n path: collection.path,\n filePattern: collection.filePattern ?? DEFAULT_FILE_PATTERN,\n previewUrl: collection.previewUrl ?? `/${collection.name}/{slug}`,\n schema: collection.schema ?? {},\n images: collection.images\n ? { ...DEFAULT_IMAGE_CONFIG, ...collection.images }\n : DEFAULT_IMAGE_CONFIG,\n };\n}\n\n/**\n * Apply defaults to the main Writenex configuration\n *\n * @param config - Partial Writenex configuration\n * @returns Configuration with all defaults applied\n */\nexport function applyConfigDefaults(\n config: WritenexConfig = {}\n): Required<WritenexConfig> {\n return {\n collections: (config.collections ?? []).map(applyCollectionDefaults),\n images: config.images\n ? { ...DEFAULT_IMAGE_CONFIG, ...config.images }\n : DEFAULT_IMAGE_CONFIG,\n editor: config.editor\n ? { ...DEFAULT_EDITOR_CONFIG, ...config.editor }\n : DEFAULT_EDITOR_CONFIG,\n discovery: config.discovery\n ? { ...DEFAULT_DISCOVERY_CONFIG, ...config.discovery }\n : DEFAULT_DISCOVERY_CONFIG,\n versionHistory: config.versionHistory\n ? { ...DEFAULT_VERSION_HISTORY_CONFIG, ...config.versionHistory }\n : DEFAULT_VERSION_HISTORY_CONFIG,\n };\n}\n"],"mappings":";AAsBO,IAAM,uBAA8C;AAAA,EACzD,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAAa;AACf;AAKO,IAAM,wBAAgD;AAAA,EAC3D,UAAU;AAAA,EACV,kBAAkB;AACpB;AAKO,IAAM,2BAAsD;AAAA,EACjE,SAAS;AAAA,EACT,QAAQ,CAAC,sBAAsB,cAAc,YAAY;AAC3D;AAKO,IAAM,iCAAiE;AAAA,EAC5E,SAAS;AAAA,EACT,aAAa;AAAA,EACb,aAAa;AACf;AAKO,IAAM,uBAAuB;AAK7B,IAAM,uBAAuB;AAQ7B,SAAS,wBACd,YAC4B;AAC5B,SAAO;AAAA,IACL,MAAM,WAAW;AAAA,IACjB,MAAM,WAAW;AAAA,IACjB,aAAa,WAAW,eAAe;AAAA,IACvC,YAAY,WAAW,cAAc,IAAI,WAAW,IAAI;AAAA,IACxD,QAAQ,WAAW,UAAU,CAAC;AAAA,IAC9B,QAAQ,WAAW,SACf,EAAE,GAAG,sBAAsB,GAAG,WAAW,OAAO,IAChD;AAAA,EACN;AACF;AAQO,SAAS,oBACd,SAAyB,CAAC,GACA;AAC1B,SAAO;AAAA,IACL,cAAc,OAAO,eAAe,CAAC,GAAG,IAAI,uBAAuB;AAAA,IACnE,QAAQ,OAAO,SACX,EAAE,GAAG,sBAAsB,GAAG,OAAO,OAAO,IAC5C;AAAA,IACJ,QAAQ,OAAO,SACX,EAAE,GAAG,uBAAuB,GAAG,OAAO,OAAO,IAC7C;AAAA,IACJ,WAAW,OAAO,YACd,EAAE,GAAG,0BAA0B,GAAG,OAAO,UAAU,IACnD;AAAA,IACJ,gBAAgB,OAAO,iBACnB,EAAE,GAAG,gCAAgC,GAAG,OAAO,eAAe,IAC9D;AAAA,EACN;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/config/defaults.ts"],"sourcesContent":["/**\n * @fileoverview Default configuration values for @writenex/astro\n *\n * This module provides default values for all configuration options.\n * These defaults are applied when loading configuration to ensure\n * all required values are present.\n *\n * @module @writenex/astro/config/defaults\n */\n\nimport type {\n CollectionConfig,\n DiscoveryConfig,\n EditorConfig,\n ImageConfig,\n VersionHistoryConfig,\n WritenexConfig,\n} from \"@/types\";\n\n/**\n * Default image configuration\n */\nexport const DEFAULT_IMAGE_CONFIG: Required<ImageConfig> = {\n strategy: \"colocated\",\n publicPath: \"/images\",\n storagePath: \"public/images\",\n};\n\n/**\n * Default editor configuration\n */\nexport const DEFAULT_EDITOR_CONFIG: Required<EditorConfig> = {\n autosave: true,\n autosaveInterval: 3000,\n};\n\n/**\n * Default discovery configuration\n */\nexport const DEFAULT_DISCOVERY_CONFIG: Required<DiscoveryConfig> = {\n enabled: true,\n ignore: [\"**/node_modules/**\", \"**/.git/**\", \"**/dist/**\"],\n};\n\n/**\n * Default version history configuration\n */\nexport const DEFAULT_VERSION_HISTORY_CONFIG: Required<VersionHistoryConfig> = {\n enabled: true,\n maxVersions: 20,\n storagePath: \".writenex/versions\",\n};\n\n/**\n * Default file pattern for content files\n */\nexport const DEFAULT_FILE_PATTERN = \"{slug}.md\";\n\n/**\n * Default content directory path\n */\nexport const DEFAULT_CONTENT_PATH = \"src/content\";\n\n/**\n * Apply defaults to a collection configuration\n *\n * @param collection - Partial collection configuration\n * @returns Collection configuration with defaults applied\n */\nexport function applyCollectionDefaults(\n collection: CollectionConfig\n): Required<CollectionConfig> {\n return {\n name: collection.name,\n path: collection.path,\n filePattern: collection.filePattern ?? DEFAULT_FILE_PATTERN,\n previewUrl: collection.previewUrl ?? `/${collection.name}/{slug}`,\n schema: collection.schema ?? {},\n images: collection.images\n ? { ...DEFAULT_IMAGE_CONFIG, ...collection.images }\n : DEFAULT_IMAGE_CONFIG,\n };\n}\n\n/**\n * Apply defaults to the main Writenex configuration\n *\n * @param config - Partial Writenex configuration\n * @returns Configuration with all defaults applied\n */\nexport function applyConfigDefaults(\n config: WritenexConfig = {}\n): Required<WritenexConfig> {\n return {\n collections: (config.collections ?? []).map(applyCollectionDefaults),\n singletons: (config.singletons ?? []).map(applyCollectionDefaults),\n images: config.images\n ? { ...DEFAULT_IMAGE_CONFIG, ...config.images }\n : DEFAULT_IMAGE_CONFIG,\n editor: config.editor\n ? { ...DEFAULT_EDITOR_CONFIG, ...config.editor }\n : DEFAULT_EDITOR_CONFIG,\n discovery: config.discovery\n ? { ...DEFAULT_DISCOVERY_CONFIG, ...config.discovery }\n : DEFAULT_DISCOVERY_CONFIG,\n versionHistory: config.versionHistory\n ? { ...DEFAULT_VERSION_HISTORY_CONFIG, ...config.versionHistory }\n : DEFAULT_VERSION_HISTORY_CONFIG,\n };\n}\n"],"mappings":";AAsBO,IAAM,uBAA8C;AAAA,EACzD,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAAa;AACf;AAKO,IAAM,wBAAgD;AAAA,EAC3D,UAAU;AAAA,EACV,kBAAkB;AACpB;AAKO,IAAM,2BAAsD;AAAA,EACjE,SAAS;AAAA,EACT,QAAQ,CAAC,sBAAsB,cAAc,YAAY;AAC3D;AAKO,IAAM,iCAAiE;AAAA,EAC5E,SAAS;AAAA,EACT,aAAa;AAAA,EACb,aAAa;AACf;AAKO,IAAM,uBAAuB;AAK7B,IAAM,uBAAuB;AAQ7B,SAAS,wBACd,YAC4B;AAC5B,SAAO;AAAA,IACL,MAAM,WAAW;AAAA,IACjB,MAAM,WAAW;AAAA,IACjB,aAAa,WAAW,eAAe;AAAA,IACvC,YAAY,WAAW,cAAc,IAAI,WAAW,IAAI;AAAA,IACxD,QAAQ,WAAW,UAAU,CAAC;AAAA,IAC9B,QAAQ,WAAW,SACf,EAAE,GAAG,sBAAsB,GAAG,WAAW,OAAO,IAChD;AAAA,EACN;AACF;AAQO,SAAS,oBACd,SAAyB,CAAC,GACA;AAC1B,SAAO;AAAA,IACL,cAAc,OAAO,eAAe,CAAC,GAAG,IAAI,uBAAuB;AAAA,IACnE,aAAa,OAAO,cAAc,CAAC,GAAG,IAAI,uBAAuB;AAAA,IACjE,QAAQ,OAAO,SACX,EAAE,GAAG,sBAAsB,GAAG,OAAO,OAAO,IAC5C;AAAA,IACJ,QAAQ,OAAO,SACX,EAAE,GAAG,uBAAuB,GAAG,OAAO,OAAO,IAC7C;AAAA,IACJ,WAAW,OAAO,YACd,EAAE,GAAG,0BAA0B,GAAG,OAAO,UAAU,IACnD;AAAA,IACJ,gBAAgB,OAAO,iBACnB,EAAE,GAAG,gCAAgC,GAAG,OAAO,eAAe,IAC9D;AAAA,EACN;AACF;","names":[]}
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
detectFilePattern,
|
|
3
3
|
getCollectionCount,
|
|
4
4
|
readCollection
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-GYAFIVVI.js";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_FILE_PATTERN
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-P5KMSHFP.js";
|
|
9
9
|
|
|
10
10
|
// src/discovery/schema.ts
|
|
11
11
|
var MAX_SAMPLE_FILES = 20;
|
|
@@ -21,6 +21,34 @@ var IMAGE_EXTENSIONS = [
|
|
|
21
21
|
".avif",
|
|
22
22
|
".svg"
|
|
23
23
|
];
|
|
24
|
+
var URL_PATTERN = /^https?:\/\//;
|
|
25
|
+
var SLUG_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
|
|
26
|
+
var DATETIME_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/;
|
|
27
|
+
function isUrlValue(value) {
|
|
28
|
+
if (typeof value !== "string") return false;
|
|
29
|
+
return URL_PATTERN.test(value);
|
|
30
|
+
}
|
|
31
|
+
function isSlugValue(value) {
|
|
32
|
+
if (typeof value !== "string") return false;
|
|
33
|
+
if (value.length < 2 || value.length > 100) return false;
|
|
34
|
+
return SLUG_PATTERN.test(value);
|
|
35
|
+
}
|
|
36
|
+
function isDatetimeValue(value) {
|
|
37
|
+
if (typeof value !== "string") return false;
|
|
38
|
+
return DATETIME_PATTERN.test(value);
|
|
39
|
+
}
|
|
40
|
+
function isFilePath(value) {
|
|
41
|
+
if (typeof value !== "string") return false;
|
|
42
|
+
if (isImagePath(value)) return false;
|
|
43
|
+
return /\.\w{2,5}$/.test(value);
|
|
44
|
+
}
|
|
45
|
+
function isMultiselectArray(values) {
|
|
46
|
+
if (values.length === 0) return false;
|
|
47
|
+
const allStrings = values.every((v) => typeof v === "string" && v.length > 0);
|
|
48
|
+
if (!allStrings) return false;
|
|
49
|
+
const uniqueValues = [...new Set(values)];
|
|
50
|
+
return uniqueValues.length <= ENUM_MAX_VALUES && values.length > uniqueValues.length;
|
|
51
|
+
}
|
|
24
52
|
function isImagePath(value) {
|
|
25
53
|
if (typeof value !== "string") return false;
|
|
26
54
|
const lowered = value.toLowerCase();
|
|
@@ -46,7 +74,17 @@ function detectValueType(value) {
|
|
|
46
74
|
}
|
|
47
75
|
function inferFieldType(analysis) {
|
|
48
76
|
if (analysis.hasImagePaths) return "image";
|
|
77
|
+
if (analysis.hasDatetimeValues) return "datetime";
|
|
49
78
|
if (analysis.hasDateValues) return "date";
|
|
79
|
+
if (analysis.hasFilePaths) return "file";
|
|
80
|
+
if (analysis.hasUrlValues) return "url";
|
|
81
|
+
if (analysis.hasSlugValues && analysis.types.size === 1) {
|
|
82
|
+
const typesArr = [...analysis.types];
|
|
83
|
+
if (typesArr.length === 1 && typesArr[0] === "string") {
|
|
84
|
+
return "slug";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (analysis.isMultiselect) return "multiselect";
|
|
50
88
|
const nonNullTypes = new Set([...analysis.types].filter((t) => t !== "null"));
|
|
51
89
|
if (nonNullTypes.size === 1) {
|
|
52
90
|
const type = [...nonNullTypes][0];
|
|
@@ -54,7 +92,7 @@ function inferFieldType(analysis) {
|
|
|
54
92
|
case "boolean":
|
|
55
93
|
return "boolean";
|
|
56
94
|
case "number":
|
|
57
|
-
return "number";
|
|
95
|
+
return analysis.allIntegers ? "integer" : "number";
|
|
58
96
|
case "array":
|
|
59
97
|
return "array";
|
|
60
98
|
case "object":
|
|
@@ -112,6 +150,12 @@ async function detectSchema(collectionPath) {
|
|
|
112
150
|
values: [],
|
|
113
151
|
hasImagePaths: false,
|
|
114
152
|
hasDateValues: false,
|
|
153
|
+
hasUrlValues: false,
|
|
154
|
+
hasSlugValues: false,
|
|
155
|
+
hasDatetimeValues: false,
|
|
156
|
+
hasFilePaths: false,
|
|
157
|
+
allIntegers: true,
|
|
158
|
+
isMultiselect: false,
|
|
115
159
|
arrayItemTypes: /* @__PURE__ */ new Set()
|
|
116
160
|
};
|
|
117
161
|
fieldAnalyses.set(fieldName, analysis);
|
|
@@ -125,10 +169,28 @@ async function detectSchema(collectionPath) {
|
|
|
125
169
|
if (isDateValue(value)) {
|
|
126
170
|
analysis.hasDateValues = true;
|
|
127
171
|
}
|
|
172
|
+
if (isUrlValue(value)) {
|
|
173
|
+
analysis.hasUrlValues = true;
|
|
174
|
+
}
|
|
175
|
+
if (isSlugValue(value)) {
|
|
176
|
+
analysis.hasSlugValues = true;
|
|
177
|
+
}
|
|
178
|
+
if (isDatetimeValue(value)) {
|
|
179
|
+
analysis.hasDatetimeValues = true;
|
|
180
|
+
}
|
|
181
|
+
if (isFilePath(value)) {
|
|
182
|
+
analysis.hasFilePaths = true;
|
|
183
|
+
}
|
|
184
|
+
if (typeof value === "number" && !Number.isInteger(value)) {
|
|
185
|
+
analysis.allIntegers = false;
|
|
186
|
+
}
|
|
128
187
|
if (Array.isArray(value)) {
|
|
129
188
|
for (const item2 of value) {
|
|
130
189
|
analysis.arrayItemTypes.add(detectValueType(item2));
|
|
131
190
|
}
|
|
191
|
+
if (isMultiselectArray(value)) {
|
|
192
|
+
analysis.isMultiselect = true;
|
|
193
|
+
}
|
|
132
194
|
}
|
|
133
195
|
}
|
|
134
196
|
}
|
|
@@ -307,4 +369,4 @@ export {
|
|
|
307
369
|
getCollection,
|
|
308
370
|
collectionExists
|
|
309
371
|
};
|
|
310
|
-
//# sourceMappingURL=chunk-
|
|
372
|
+
//# sourceMappingURL=chunk-XVQNYPOI.js.map
|