@imjp/writenex-astro 0.1.0 → 1.3.6
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/README.md +13 -13
- package/dist/{chunk-CF2XXJFF.js → chunk-4H63L4YO.js} +436 -436
- package/dist/chunk-4H63L4YO.js.map +1 -0
- package/dist/{chunk-AAOQHQPU.js → chunk-GYAFIVVI.js} +6 -6
- package/dist/chunk-GYAFIVVI.js.map +1 -0
- package/dist/{chunk-XNTQTTJU.js → chunk-JFQQJPDF.js} +2 -2
- package/dist/{chunk-XNTQTTJU.js.map → chunk-JFQQJPDF.js.map} +1 -1
- package/dist/{chunk-CYLDJ3HZ.js → chunk-JMNCPNQX.js} +4 -4
- package/dist/{chunk-CYLDJ3HZ.js.map → chunk-JMNCPNQX.js.map} +1 -1
- package/dist/{chunk-5PM6EQE5.js → chunk-N37EPLKG.js} +13 -5
- package/dist/chunk-N37EPLKG.js.map +1 -0
- package/dist/{chunk-7XU5X6CW.js → chunk-NSW7AIVF.js} +12 -12
- package/dist/chunk-NSW7AIVF.js.map +1 -0
- package/dist/{chunk-CRPZUUDU.js → chunk-YBCPOLMY.js} +1 -1
- package/dist/{chunk-CRPZUUDU.js.map → chunk-YBCPOLMY.js.map} +1 -1
- package/dist/client/index.css +1 -1
- package/dist/client/index.css.map +1 -1
- package/dist/client/index.d.ts +19 -0
- package/dist/client/index.js +159 -147
- package/dist/client/index.js.map +1 -1
- package/dist/client/styles.css +2 -8
- package/dist/config/index.d.ts +2 -2
- package/dist/config/index.js +2 -2
- package/dist/{config-BmEdBDo_.d.ts → config-CliL0CoN.d.ts} +1 -1
- package/dist/{content-BWR52vD-.d.ts → content-TuL3GT66.d.ts} +1 -1
- package/dist/discovery/index.d.ts +2 -2
- package/dist/discovery/index.js +3 -3
- package/dist/filesystem/index.d.ts +703 -703
- package/dist/filesystem/index.js +4 -4
- package/dist/filesystem/index.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/{loader-55LWCXHA.js → loader-53VVP2IN.js} +3 -3
- package/dist/schema-DDJyoVkj.d.ts +189 -0
- package/dist/server/index.d.ts +37 -37
- package/dist/server/index.js +5 -5
- package/package.json +17 -18
- package/src/client/App.tsx +18 -18
- package/src/client/components/ConfigPanel/ConfigPanel.tsx +14 -13
- package/src/client/components/CreateContentModal/CreateContentModal.tsx +1 -1
- package/src/client/components/Editor/Editor.tsx +27 -27
- package/src/client/components/Editor/ImageDialog.tsx +4 -3
- package/src/client/components/Editor/LinkDialog.tsx +7 -6
- package/src/client/components/Editor/index.ts +1 -1
- package/src/client/components/FrontmatterForm/FrontmatterForm.css +1 -1
- package/src/client/components/FrontmatterForm/FrontmatterForm.tsx +1 -1
- package/src/client/components/Header/Header.tsx +8 -8
- package/src/client/components/KeyboardShortcuts/KeyboardShortcuts.tsx +1 -1
- package/src/client/components/LazyEditor.tsx +1 -1
- package/src/client/components/LiveRegion/index.ts +1 -1
- package/src/client/components/SearchReplace/SearchReplacePanel.tsx +5 -5
- package/src/client/components/SearchReplace/index.ts +1 -1
- package/src/client/components/SelectCollectionModal/SelectCollectionModal.tsx +2 -2
- package/src/client/components/Sidebar/Sidebar.tsx +6 -6
- package/src/client/components/SkipLink/index.ts +1 -1
- package/src/client/components/UnsavedChangesModal/UnsavedChangesModal.tsx +1 -1
- package/src/client/components/VersionHistory/DiffViewer.tsx +18 -11
- package/src/client/components/VersionHistory/VersionActions.tsx +6 -6
- package/src/client/components/VersionHistory/VersionHistoryPanel.tsx +10 -10
- package/src/client/components/VersionHistory/index.ts +2 -2
- package/src/client/context/ApiContext.tsx +2 -2
- package/src/client/context/ThemeContext.tsx +2 -2
- package/src/client/hooks/useApi.ts +1 -1
- package/src/client/hooks/useFocusTrap.ts +1 -1
- package/src/client/hooks/useSearch.ts +1 -1
- package/src/client/hooks/useVersionHistory.ts +2 -2
- package/src/client/index.tsx +1 -1
- package/src/client/styles.css +2 -8
- package/src/config/defaults.ts +4 -4
- package/src/config/index.ts +14 -16
- package/src/config/loader.ts +24 -4
- package/src/config/schema.ts +8 -4
- package/src/core/index.ts +1 -1
- package/src/discovery/collections.ts +3 -3
- package/src/discovery/index.ts +9 -11
- package/src/discovery/patterns.ts +2 -2
- package/src/discovery/schema.ts +1 -1
- package/src/filesystem/images.ts +3 -3
- package/src/filesystem/index.ts +74 -79
- package/src/filesystem/reader.ts +5 -3
- package/src/filesystem/version-config.ts +10 -10
- package/src/filesystem/versions.ts +9 -9
- package/src/filesystem/watcher.ts +1 -1
- package/src/filesystem/writer.ts +6 -6
- package/src/global.d.ts +39 -0
- package/src/index.ts +10 -10
- package/src/integration.ts +6 -3
- package/src/server/assets.ts +3 -3
- package/src/server/cache.ts +1 -1
- package/src/server/index.ts +12 -15
- package/src/server/middleware.ts +3 -3
- package/src/server/routes.ts +28 -28
- package/src/types/index.ts +24 -28
- package/dist/chunk-5PM6EQE5.js.map +0 -1
- package/dist/chunk-7XU5X6CW.js.map +0 -1
- package/dist/chunk-AAOQHQPU.js.map +0 -1
- package/dist/chunk-CF2XXJFF.js.map +0 -1
- package/dist/loader-CrdnaAWR.d.ts +0 -327
- /package/dist/{loader-55LWCXHA.js.map → loader-53VVP2IN.js.map} +0 -0
|
@@ -1,7 +1,255 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { V as VersionHistoryConfig, d as SaveVersionOptions, e as VersionResult, f as VersionEntry, g as Version, R as RestoreVersionOptions, h as RestoreResult, i as VersionManifest, I as ImageConfig } from '../config-BmEdBDo_.js';
|
|
3
|
-
import { C as ContentConflictError } from '../errors-C0iYiDTv.js';
|
|
1
|
+
import { I as ImageConfig, V as VersionHistoryConfig, d as VersionResult, e as Version, f as VersionEntry, R as RestoreVersionOptions, g as RestoreResult, h as SaveVersionOptions, i as VersionManifest } from '../config-CliL0CoN.js';
|
|
4
2
|
import { I as ImageDiscoveryOptions, a as ImageDiscoveryResult, D as DiscoveredImage } from '../image-FP7w5ZIs.js';
|
|
3
|
+
import { C as ContentItem, a as ContentSummary } from '../content-TuL3GT66.js';
|
|
4
|
+
import { C as ContentConflictError } from '../errors-C0iYiDTv.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @fileoverview Image handling for content collections
|
|
8
|
+
*
|
|
9
|
+
* This module provides functions for uploading and managing images
|
|
10
|
+
* in content collections with support for different storage strategies.
|
|
11
|
+
*
|
|
12
|
+
* ## Strategies:
|
|
13
|
+
* - colocated: Images stored alongside content files
|
|
14
|
+
* - public: Images stored in public directory
|
|
15
|
+
* - custom: User-defined storage paths
|
|
16
|
+
*
|
|
17
|
+
* @module @writenex/astro/filesystem/images
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Default image configuration
|
|
22
|
+
*/
|
|
23
|
+
declare const DEFAULT_IMAGE_CONFIG: ImageConfig;
|
|
24
|
+
/**
|
|
25
|
+
* Result of image upload operation
|
|
26
|
+
*/
|
|
27
|
+
interface ImageUploadResult {
|
|
28
|
+
success: boolean;
|
|
29
|
+
/** Markdown-compatible path for the image */
|
|
30
|
+
path?: string;
|
|
31
|
+
/** Public URL for the image */
|
|
32
|
+
url?: string;
|
|
33
|
+
/** Error message if failed */
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Options for image upload
|
|
38
|
+
*/
|
|
39
|
+
interface ImageUploadOptions {
|
|
40
|
+
/** Original filename */
|
|
41
|
+
filename: string;
|
|
42
|
+
/** Image binary data */
|
|
43
|
+
data: Buffer;
|
|
44
|
+
/** Collection name */
|
|
45
|
+
collection: string;
|
|
46
|
+
/** Content ID (slug) */
|
|
47
|
+
contentId: string;
|
|
48
|
+
/** Project root path */
|
|
49
|
+
projectRoot: string;
|
|
50
|
+
/** Image configuration */
|
|
51
|
+
config?: ImageConfig;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Validate image file
|
|
55
|
+
*
|
|
56
|
+
* @param filename - Original filename
|
|
57
|
+
* @returns True if valid image file
|
|
58
|
+
*/
|
|
59
|
+
declare function isValidImageFile(filename: string): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Upload an image file
|
|
62
|
+
*
|
|
63
|
+
* @param options - Upload options
|
|
64
|
+
* @returns Upload result with paths
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const result = await uploadImage({
|
|
69
|
+
* filename: "hero.jpg",
|
|
70
|
+
* data: imageBuffer,
|
|
71
|
+
* collection: "blog",
|
|
72
|
+
* contentId: "my-post",
|
|
73
|
+
* projectRoot: "/path/to/project",
|
|
74
|
+
* });
|
|
75
|
+
*
|
|
76
|
+
* if (result.success) {
|
|
77
|
+
* console.log(result.path); // "./my-post/hero-abc123.jpg"
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare function uploadImage(options: ImageUploadOptions): Promise<ImageUploadResult>;
|
|
82
|
+
/**
|
|
83
|
+
* Parse multipart form data for image upload
|
|
84
|
+
*
|
|
85
|
+
* Simple parser for multipart/form-data with single file upload.
|
|
86
|
+
* For production, consider using a proper multipart parser library.
|
|
87
|
+
*
|
|
88
|
+
* @param body - Raw request body
|
|
89
|
+
* @param contentType - Content-Type header
|
|
90
|
+
* @returns Parsed file data and fields
|
|
91
|
+
*/
|
|
92
|
+
declare function parseMultipartFormData(body: Buffer, contentType: string): {
|
|
93
|
+
file?: {
|
|
94
|
+
filename: string;
|
|
95
|
+
data: Buffer;
|
|
96
|
+
contentType: string;
|
|
97
|
+
};
|
|
98
|
+
fields: Record<string, string>;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Content structure type detected from file path
|
|
102
|
+
*/
|
|
103
|
+
type ContentStructure = "flat" | "folder-based" | "date-prefixed";
|
|
104
|
+
/**
|
|
105
|
+
* Result of content structure detection
|
|
106
|
+
*/
|
|
107
|
+
interface ContentStructureResult {
|
|
108
|
+
/** Detected structure type */
|
|
109
|
+
structure: ContentStructure;
|
|
110
|
+
/** Path to the image folder (null if doesn't exist) */
|
|
111
|
+
imageFolderPath: string | null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Detect content structure and get the image folder path
|
|
115
|
+
*
|
|
116
|
+
* Handles three content structures:
|
|
117
|
+
* - Flat file: `my-post.md` -> looks for `my-post/` sibling folder
|
|
118
|
+
* - Folder-based: `slug/index.md` -> uses `slug/` parent folder
|
|
119
|
+
* - Date-prefixed: `2024-01-15-my-post.md` -> looks for `2024-01-15-my-post/` sibling folder
|
|
120
|
+
*
|
|
121
|
+
* @param collectionPath - Absolute path to the collection directory
|
|
122
|
+
* @param contentId - Content ID (slug)
|
|
123
|
+
* @param contentFilePath - Absolute path to the content file
|
|
124
|
+
* @returns Path to the image folder, or null if no image folder exists
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* // Flat file structure
|
|
129
|
+
* const folder = getContentImageFolder(
|
|
130
|
+
* '/project/src/content/blog',
|
|
131
|
+
* 'my-post',
|
|
132
|
+
* '/project/src/content/blog/my-post.md'
|
|
133
|
+
* );
|
|
134
|
+
* // Returns: '/project/src/content/blog/my-post' (if exists)
|
|
135
|
+
*
|
|
136
|
+
* // Folder-based structure
|
|
137
|
+
* const folder = getContentImageFolder(
|
|
138
|
+
* '/project/src/content/blog',
|
|
139
|
+
* 'my-post',
|
|
140
|
+
* '/project/src/content/blog/my-post/index.md'
|
|
141
|
+
* );
|
|
142
|
+
* // Returns: '/project/src/content/blog/my-post'
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
declare function getContentImageFolder(collectionPath: string, contentId: string, contentFilePath: string): string | null;
|
|
146
|
+
/**
|
|
147
|
+
* Detect the content structure type from a content file path
|
|
148
|
+
*
|
|
149
|
+
* @param contentFilePath - Absolute path to the content file
|
|
150
|
+
* @returns The detected content structure type
|
|
151
|
+
*/
|
|
152
|
+
declare function detectContentStructure(contentFilePath: string): ContentStructure;
|
|
153
|
+
/**
|
|
154
|
+
* Options for recursive directory scanning
|
|
155
|
+
*/
|
|
156
|
+
interface ScanOptions {
|
|
157
|
+
/** Maximum recursion depth */
|
|
158
|
+
maxDepth: number;
|
|
159
|
+
/** Current recursion depth */
|
|
160
|
+
currentDepth: number;
|
|
161
|
+
/** Base path for calculating relative paths */
|
|
162
|
+
basePath: string;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Scan a directory recursively for image files
|
|
166
|
+
*
|
|
167
|
+
* Recursively scans the given directory for image files with supported extensions.
|
|
168
|
+
* Skips hidden folders (starting with .) and special folders (starting with _).
|
|
169
|
+
* Limits recursion to the specified maxDepth.
|
|
170
|
+
*
|
|
171
|
+
* @param dirPath - Absolute path to the directory to scan
|
|
172
|
+
* @param basePath - Base path for calculating relative paths
|
|
173
|
+
* @param options - Scan options including maxDepth and currentDepth
|
|
174
|
+
* @returns Array of discovered images
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* const images = await scanDirectoryForImages(
|
|
179
|
+
* '/project/src/content/blog/my-post',
|
|
180
|
+
* '/project/src/content/blog/my-post',
|
|
181
|
+
* { maxDepth: 5, currentDepth: 0, basePath: '/project/src/content/blog/my-post' }
|
|
182
|
+
* );
|
|
183
|
+
* ```
|
|
184
|
+
*/
|
|
185
|
+
declare function scanDirectoryForImages(dirPath: string, basePath: string, options: ScanOptions): Promise<DiscoveredImage[]>;
|
|
186
|
+
/**
|
|
187
|
+
* Calculate relative path from content file to image file
|
|
188
|
+
*
|
|
189
|
+
* Calculates the path that can be used in markdown to reference an image
|
|
190
|
+
* relative to the content file's location. The path always starts with ./
|
|
191
|
+
* to ensure it's treated as a relative reference.
|
|
192
|
+
*
|
|
193
|
+
* @param contentFilePath - Absolute path to the content file (e.g., /project/src/content/blog/my-post.md)
|
|
194
|
+
* @param imagePath - Absolute path to the image file (e.g., /project/src/content/blog/my-post/images/hero.jpg)
|
|
195
|
+
* @returns Relative path starting with ./ (e.g., ./my-post/images/hero.jpg)
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```typescript
|
|
199
|
+
* // Flat file structure
|
|
200
|
+
* const relPath = calculateRelativePath(
|
|
201
|
+
* '/project/src/content/blog/my-post.md',
|
|
202
|
+
* '/project/src/content/blog/my-post/hero.jpg'
|
|
203
|
+
* );
|
|
204
|
+
* // Returns: './my-post/hero.jpg'
|
|
205
|
+
*
|
|
206
|
+
* // Folder-based structure
|
|
207
|
+
* const relPath = calculateRelativePath(
|
|
208
|
+
* '/project/src/content/blog/my-post/index.md',
|
|
209
|
+
* '/project/src/content/blog/my-post/images/hero.jpg'
|
|
210
|
+
* );
|
|
211
|
+
* // Returns: './images/hero.jpg'
|
|
212
|
+
*
|
|
213
|
+
* // Nested subfolder
|
|
214
|
+
* const relPath = calculateRelativePath(
|
|
215
|
+
* '/project/src/content/blog/my-post/index.md',
|
|
216
|
+
* '/project/src/content/blog/my-post/assets/photos/hero.jpg'
|
|
217
|
+
* );
|
|
218
|
+
* // Returns: './assets/photos/hero.jpg'
|
|
219
|
+
* ```
|
|
220
|
+
*/
|
|
221
|
+
declare function calculateRelativePath(contentFilePath: string, imagePath: string): string;
|
|
222
|
+
/**
|
|
223
|
+
* Discover all images associated with a content item
|
|
224
|
+
*
|
|
225
|
+
* This is the main entry point for image discovery. It:
|
|
226
|
+
* 1. Locates the content file using getContentFilePath
|
|
227
|
+
* 2. Determines the image folder using getContentImageFolder
|
|
228
|
+
* 3. Scans the folder recursively using scanDirectoryForImages
|
|
229
|
+
* 4. Calculates relative paths for all discovered images
|
|
230
|
+
*
|
|
231
|
+
* @param collectionPath - Absolute path to the collection directory
|
|
232
|
+
* @param contentId - Content ID (slug)
|
|
233
|
+
* @param options - Optional discovery options
|
|
234
|
+
* @returns ImageDiscoveryResult with discovered images or error
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```typescript
|
|
238
|
+
* const result = await discoverContentImages(
|
|
239
|
+
* '/project/src/content/blog',
|
|
240
|
+
* 'my-post',
|
|
241
|
+
* { maxDepth: 3 }
|
|
242
|
+
* );
|
|
243
|
+
*
|
|
244
|
+
* if (result.success) {
|
|
245
|
+
* console.log(`Found ${result.images.length} images`);
|
|
246
|
+
* for (const img of result.images) {
|
|
247
|
+
* console.log(`- ${img.relativePath}`);
|
|
248
|
+
* }
|
|
249
|
+
* }
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
declare function discoverContentImages(collectionPath: string, contentId: string, options?: ImageDiscoveryOptions): Promise<ImageDiscoveryResult>;
|
|
5
253
|
|
|
6
254
|
/**
|
|
7
255
|
* @fileoverview Filesystem reader for content collections
|
|
@@ -171,281 +419,220 @@ declare function getFileStats(filePath: string): Promise<{
|
|
|
171
419
|
declare function getContentFilePath(collectionPath: string, contentId: string): string | null;
|
|
172
420
|
|
|
173
421
|
/**
|
|
174
|
-
* @fileoverview
|
|
175
|
-
*
|
|
176
|
-
* This module provides functions for creating, updating, and deleting
|
|
177
|
-
* content files in Astro content collections.
|
|
422
|
+
* @fileoverview Config-aware wrappers for version history operations
|
|
178
423
|
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
* - Generate unique slugs to avoid collisions
|
|
184
|
-
* - Support for different file patterns (flat, folder-based, date-prefixed)
|
|
185
|
-
* - Automatic version history creation before updates
|
|
424
|
+
* This module provides wrapper functions that automatically apply configuration
|
|
425
|
+
* defaults and check the enabled flag before performing version operations.
|
|
426
|
+
* These wrappers simplify usage by accepting partial configuration and handling
|
|
427
|
+
* all the configuration resolution internally.
|
|
186
428
|
*
|
|
187
|
-
* @module @writenex/astro/filesystem/
|
|
429
|
+
* @module @writenex/astro/filesystem/version-config
|
|
430
|
+
* @see {@link saveVersion} - Core save function
|
|
431
|
+
* @see {@link getVersions} - Core list function
|
|
188
432
|
*/
|
|
189
433
|
|
|
190
434
|
/**
|
|
191
|
-
*
|
|
192
|
-
*/
|
|
193
|
-
interface CreateContentOptions {
|
|
194
|
-
/** Frontmatter data */
|
|
195
|
-
frontmatter: Record<string, unknown>;
|
|
196
|
-
/** Markdown body content */
|
|
197
|
-
body: string;
|
|
198
|
-
/** Custom slug (optional, generated from title if not provided) */
|
|
199
|
-
slug?: string;
|
|
200
|
-
/** File pattern for the collection (e.g., "{slug}/index.md", "{date}-{slug}.md") */
|
|
201
|
-
filePattern?: string;
|
|
202
|
-
/** Custom token values to override automatic resolution */
|
|
203
|
-
customTokens?: Record<string, string>;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Options for updating content
|
|
207
|
-
*/
|
|
208
|
-
interface UpdateContentOptions {
|
|
209
|
-
/** Updated frontmatter data */
|
|
210
|
-
frontmatter?: Record<string, unknown>;
|
|
211
|
-
/** Updated markdown body content */
|
|
212
|
-
body?: string;
|
|
213
|
-
/** Project root for version history (required for version creation) */
|
|
214
|
-
projectRoot?: string;
|
|
215
|
-
/** Collection name for version history */
|
|
216
|
-
collection?: string;
|
|
217
|
-
/** Version history configuration */
|
|
218
|
-
versionHistoryConfig?: Required<VersionHistoryConfig>;
|
|
219
|
-
/**
|
|
220
|
-
* Expected modification time for conflict detection.
|
|
221
|
-
* If provided and the file's mtime differs, the update will fail with a conflict error.
|
|
222
|
-
*/
|
|
223
|
-
expectedMtime?: number;
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Result of a write operation
|
|
227
|
-
*/
|
|
228
|
-
interface WriteResult {
|
|
229
|
-
/** Whether the operation was successful */
|
|
230
|
-
success: boolean;
|
|
231
|
-
/** The content ID (slug) */
|
|
232
|
-
id?: string;
|
|
233
|
-
/** The file path */
|
|
234
|
-
path?: string;
|
|
235
|
-
/** Error message if failed */
|
|
236
|
-
error?: string;
|
|
237
|
-
/** New modification time after write (for conflict detection) */
|
|
238
|
-
mtime?: number;
|
|
239
|
-
/** Conflict error if update failed due to external modification */
|
|
240
|
-
conflict?: ContentConflictError;
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Generate a URL-safe slug from a string
|
|
435
|
+
* Resolve version history configuration with defaults applied.
|
|
244
436
|
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
437
|
+
* Takes a partial configuration and merges it with defaults to produce
|
|
438
|
+
* a complete configuration object.
|
|
439
|
+
*
|
|
440
|
+
* @param config - Partial version history configuration
|
|
441
|
+
* @returns Complete configuration with all defaults applied
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```typescript
|
|
445
|
+
* const resolved = resolveVersionConfig({ maxVersions: 50 });
|
|
446
|
+
* // Returns: { enabled: true, maxVersions: 50, storagePath: '.writenex/versions' }
|
|
447
|
+
* ```
|
|
247
448
|
*/
|
|
248
|
-
declare function
|
|
449
|
+
declare function resolveVersionConfig(config?: VersionHistoryConfig): Required<VersionHistoryConfig>;
|
|
249
450
|
/**
|
|
250
|
-
*
|
|
451
|
+
* Check if version history is enabled in the configuration.
|
|
251
452
|
*
|
|
252
|
-
* @param
|
|
253
|
-
* @
|
|
254
|
-
*
|
|
255
|
-
* @
|
|
453
|
+
* @param config - Version history configuration (partial or full)
|
|
454
|
+
* @returns True if version history is enabled
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```typescript
|
|
458
|
+
* if (isVersionHistoryEnabled({ enabled: false })) {
|
|
459
|
+
* // This won't execute
|
|
460
|
+
* }
|
|
461
|
+
* ```
|
|
256
462
|
*/
|
|
257
|
-
declare function
|
|
463
|
+
declare function isVersionHistoryEnabled(config?: VersionHistoryConfig): boolean;
|
|
258
464
|
/**
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* Supports various file patterns with automatic token resolution:
|
|
262
|
-
* - `{slug}.md` - Simple flat structure (default)
|
|
263
|
-
* - `{slug}/index.md` - Folder-based content
|
|
264
|
-
* - `{date}-{slug}.md` - Date-prefixed naming
|
|
265
|
-
* - `{year}/{slug}.md` - Year folder structure
|
|
266
|
-
* - `{year}/{month}/{slug}.md` - Year/month folder structure
|
|
267
|
-
* - `{year}/{month}/{day}/{slug}.md` - Full date folder structure
|
|
268
|
-
* - `{lang}/{slug}.md` - Language-prefixed (i18n)
|
|
269
|
-
* - `{category}/{slug}.md` - Category folder structure
|
|
270
|
-
* - `{author}/{slug}.md` - Author folder structure
|
|
271
|
-
* - Any custom pattern with tokens from frontmatter
|
|
465
|
+
* Save a version with automatic configuration resolution.
|
|
272
466
|
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
* 2. Known token resolvers (date, year, month, lang, category, etc.)
|
|
276
|
-
* 3. Frontmatter values (for custom tokens)
|
|
277
|
-
* 4. Default values
|
|
467
|
+
* This wrapper automatically applies configuration defaults and checks
|
|
468
|
+
* the enabled flag before delegating to the core saveVersion function.
|
|
278
469
|
*
|
|
279
|
-
* @param
|
|
280
|
-
* @param
|
|
281
|
-
* @
|
|
470
|
+
* @param projectRoot - Absolute path to project root
|
|
471
|
+
* @param collection - Collection name
|
|
472
|
+
* @param contentId - Content item ID (slug)
|
|
473
|
+
* @param content - Full markdown content to save
|
|
474
|
+
* @param config - Partial version history configuration
|
|
475
|
+
* @param options - Save options
|
|
476
|
+
* @returns Result of the save operation
|
|
282
477
|
*
|
|
283
478
|
* @example
|
|
284
479
|
* ```typescript
|
|
285
|
-
* //
|
|
286
|
-
* const result = await
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
480
|
+
* // With partial config - defaults are applied automatically
|
|
481
|
+
* const result = await saveVersionWithConfig(
|
|
482
|
+
* '/project',
|
|
483
|
+
* 'blog',
|
|
484
|
+
* 'my-post',
|
|
485
|
+
* '---\ntitle: My Post\n---\n\nContent...',
|
|
486
|
+
* { maxVersions: 50 } // enabled and storagePath use defaults
|
|
487
|
+
* );
|
|
488
|
+
* ```
|
|
489
|
+
*/
|
|
490
|
+
declare function saveVersionWithConfig(projectRoot: string, collection: string, contentId: string, content: string, config?: VersionHistoryConfig, options?: SaveVersionOptions): Promise<VersionResult>;
|
|
491
|
+
/**
|
|
492
|
+
* Get all versions with automatic configuration resolution.
|
|
290
493
|
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
* frontmatter: { title: 'My New Post', pubDate: new Date() },
|
|
294
|
-
* body: '# Hello World',
|
|
295
|
-
* filePattern: '{slug}/index.md',
|
|
296
|
-
* });
|
|
494
|
+
* This wrapper automatically applies configuration defaults and checks
|
|
495
|
+
* the enabled flag before delegating to the core getVersions function.
|
|
297
496
|
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* });
|
|
497
|
+
* @param projectRoot - Absolute path to project root
|
|
498
|
+
* @param collection - Collection name
|
|
499
|
+
* @param contentId - Content item ID (slug)
|
|
500
|
+
* @param config - Partial version history configuration
|
|
501
|
+
* @returns Array of version entries (empty if disabled)
|
|
304
502
|
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
* }
|
|
503
|
+
* @example
|
|
504
|
+
* ```typescript
|
|
505
|
+
* const versions = await getVersionsWithConfig(
|
|
506
|
+
* '/project',
|
|
507
|
+
* 'blog',
|
|
508
|
+
* 'my-post',
|
|
509
|
+
* { storagePath: 'custom/versions' }
|
|
510
|
+
* );
|
|
312
511
|
* ```
|
|
313
512
|
*/
|
|
314
|
-
declare function
|
|
513
|
+
declare function getVersionsWithConfig(projectRoot: string, collection: string, contentId: string, config?: VersionHistoryConfig): Promise<VersionEntry[]>;
|
|
315
514
|
/**
|
|
316
|
-
*
|
|
515
|
+
* Get a specific version with automatic configuration resolution.
|
|
317
516
|
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* file write if the new content is identical to the current file content.
|
|
321
|
-
* Version creation errors are logged but do not fail the save operation.
|
|
517
|
+
* This wrapper automatically applies configuration defaults and checks
|
|
518
|
+
* the enabled flag before delegating to the core getVersion function.
|
|
322
519
|
*
|
|
323
|
-
* @param
|
|
324
|
-
* @param
|
|
325
|
-
* @param
|
|
326
|
-
* @
|
|
520
|
+
* @param projectRoot - Absolute path to project root
|
|
521
|
+
* @param collection - Collection name
|
|
522
|
+
* @param contentId - Content item ID (slug)
|
|
523
|
+
* @param versionId - Version ID to retrieve
|
|
524
|
+
* @param config - Partial version history configuration
|
|
525
|
+
* @returns Full version data or null if not found/disabled
|
|
327
526
|
*
|
|
328
527
|
* @example
|
|
329
528
|
* ```typescript
|
|
330
|
-
* const
|
|
331
|
-
* '/project
|
|
332
|
-
* '
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
* body: '# Updated Content',
|
|
336
|
-
* projectRoot: '/project',
|
|
337
|
-
* collection: 'blog',
|
|
338
|
-
* versionHistoryConfig: { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' },
|
|
339
|
-
* }
|
|
529
|
+
* const version = await getVersionWithConfig(
|
|
530
|
+
* '/project',
|
|
531
|
+
* 'blog',
|
|
532
|
+
* 'my-post',
|
|
533
|
+
* '2024-12-11T10-30-00-000Z'
|
|
340
534
|
* );
|
|
341
535
|
* ```
|
|
342
536
|
*/
|
|
343
|
-
declare function
|
|
537
|
+
declare function getVersionWithConfig(projectRoot: string, collection: string, contentId: string, versionId: string, config?: VersionHistoryConfig): Promise<Version | null>;
|
|
344
538
|
/**
|
|
345
|
-
* Delete a
|
|
539
|
+
* Delete a version with automatic configuration resolution.
|
|
346
540
|
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
541
|
+
* This wrapper automatically applies configuration defaults before
|
|
542
|
+
* delegating to the core deleteVersion function.
|
|
543
|
+
*
|
|
544
|
+
* @param projectRoot - Absolute path to project root
|
|
545
|
+
* @param collection - Collection name
|
|
546
|
+
* @param contentId - Content item ID (slug)
|
|
547
|
+
* @param versionId - Version ID to delete
|
|
548
|
+
* @param config - Partial version history configuration
|
|
549
|
+
* @returns Result of the delete operation
|
|
349
550
|
*
|
|
350
551
|
* @example
|
|
351
552
|
* ```typescript
|
|
352
|
-
* const result = await
|
|
553
|
+
* const result = await deleteVersionWithConfig(
|
|
554
|
+
* '/project',
|
|
555
|
+
* 'blog',
|
|
556
|
+
* 'my-post',
|
|
557
|
+
* '2024-12-11T10-30-00-000Z'
|
|
558
|
+
* );
|
|
353
559
|
* ```
|
|
354
560
|
*/
|
|
355
|
-
declare function
|
|
356
|
-
|
|
561
|
+
declare function deleteVersionWithConfig(projectRoot: string, collection: string, contentId: string, versionId: string, config?: VersionHistoryConfig): Promise<VersionResult>;
|
|
357
562
|
/**
|
|
358
|
-
*
|
|
563
|
+
* Clear all versions with automatic configuration resolution.
|
|
359
564
|
*
|
|
360
|
-
* This
|
|
361
|
-
*
|
|
362
|
-
* (e.g., in VS Code or another editor).
|
|
565
|
+
* This wrapper automatically applies configuration defaults before
|
|
566
|
+
* delegating to the core clearVersions function.
|
|
363
567
|
*
|
|
364
|
-
* @
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
*
|
|
368
|
-
|
|
369
|
-
type FileChangeType = "add" | "change" | "unlink";
|
|
370
|
-
/**
|
|
371
|
-
* File change event
|
|
372
|
-
*/
|
|
373
|
-
interface FileChangeEvent {
|
|
374
|
-
type: FileChangeType;
|
|
375
|
-
path: string;
|
|
376
|
-
collection: string;
|
|
377
|
-
}
|
|
378
|
-
/**
|
|
379
|
-
* Watcher options
|
|
380
|
-
*/
|
|
381
|
-
interface WatcherOptions {
|
|
382
|
-
/** Callback when a file changes */
|
|
383
|
-
onChange?: (event: FileChangeEvent) => void;
|
|
384
|
-
/** Debounce delay in milliseconds */
|
|
385
|
-
debounceMs?: number;
|
|
386
|
-
/** Patterns to ignore */
|
|
387
|
-
ignored?: string[];
|
|
388
|
-
}
|
|
389
|
-
/**
|
|
390
|
-
* Content file watcher
|
|
568
|
+
* @param projectRoot - Absolute path to project root
|
|
569
|
+
* @param collection - Collection name
|
|
570
|
+
* @param contentId - Content item ID (slug)
|
|
571
|
+
* @param config - Partial version history configuration
|
|
572
|
+
* @returns Result of the clear operation
|
|
391
573
|
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
574
|
+
* @example
|
|
575
|
+
* ```typescript
|
|
576
|
+
* const result = await clearVersionsWithConfig(
|
|
577
|
+
* '/project',
|
|
578
|
+
* 'blog',
|
|
579
|
+
* 'my-post'
|
|
580
|
+
* );
|
|
581
|
+
* ```
|
|
394
582
|
*/
|
|
395
|
-
declare
|
|
396
|
-
private watcher;
|
|
397
|
-
private projectRoot;
|
|
398
|
-
private contentDir;
|
|
399
|
-
private options;
|
|
400
|
-
private debounceTimers;
|
|
401
|
-
constructor(projectRoot: string, contentDir?: string, options?: WatcherOptions);
|
|
402
|
-
/**
|
|
403
|
-
* Start watching for file changes
|
|
404
|
-
*/
|
|
405
|
-
start(): void;
|
|
406
|
-
/**
|
|
407
|
-
* Stop watching for file changes
|
|
408
|
-
*/
|
|
409
|
-
stop(): Promise<void>;
|
|
410
|
-
/**
|
|
411
|
-
* Handle a file change event
|
|
412
|
-
*/
|
|
413
|
-
private handleChange;
|
|
414
|
-
/**
|
|
415
|
-
* Emit a file change event
|
|
416
|
-
*/
|
|
417
|
-
private emitChange;
|
|
418
|
-
/**
|
|
419
|
-
* Check if the watcher is running
|
|
420
|
-
*/
|
|
421
|
-
isWatching(): boolean;
|
|
422
|
-
}
|
|
583
|
+
declare function clearVersionsWithConfig(projectRoot: string, collection: string, contentId: string, config?: VersionHistoryConfig): Promise<VersionResult>;
|
|
423
584
|
/**
|
|
424
|
-
*
|
|
585
|
+
* Prune old versions with automatic configuration resolution.
|
|
586
|
+
*
|
|
587
|
+
* This wrapper automatically applies configuration defaults before
|
|
588
|
+
* delegating to the core pruneVersions function. Uses the configured
|
|
589
|
+
* maxVersions value for determining how many versions to keep.
|
|
590
|
+
*
|
|
591
|
+
* @param projectRoot - Absolute path to project root
|
|
592
|
+
* @param collection - Collection name
|
|
593
|
+
* @param contentId - Content item ID (slug)
|
|
594
|
+
* @param config - Partial version history configuration
|
|
595
|
+
* @returns Result of the prune operation
|
|
596
|
+
*
|
|
597
|
+
* @example
|
|
598
|
+
* ```typescript
|
|
599
|
+
* // Uses custom maxVersions
|
|
600
|
+
* const result = await pruneVersionsWithConfig(
|
|
601
|
+
* '/project',
|
|
602
|
+
* 'blog',
|
|
603
|
+
* 'my-post',
|
|
604
|
+
* { maxVersions: 10 }
|
|
605
|
+
* );
|
|
606
|
+
* ```
|
|
425
607
|
*/
|
|
426
|
-
declare
|
|
427
|
-
private mtimes;
|
|
428
|
-
/**
|
|
429
|
-
* Record the current modification time of a file
|
|
430
|
-
*/
|
|
431
|
-
track(filePath: string): Promise<void>;
|
|
432
|
-
/**
|
|
433
|
-
* Check if a file has been modified externally
|
|
434
|
-
*/
|
|
435
|
-
hasExternalChanges(filePath: string): Promise<boolean>;
|
|
436
|
-
/**
|
|
437
|
-
* Clear tracking for a file
|
|
438
|
-
*/
|
|
439
|
-
untrack(filePath: string): void;
|
|
440
|
-
/**
|
|
441
|
-
* Clear all tracking
|
|
442
|
-
*/
|
|
443
|
-
clear(): void;
|
|
444
|
-
}
|
|
608
|
+
declare function pruneVersionsWithConfig(projectRoot: string, collection: string, contentId: string, config?: VersionHistoryConfig): Promise<VersionResult>;
|
|
445
609
|
/**
|
|
446
|
-
*
|
|
610
|
+
* Restore a version with automatic configuration resolution.
|
|
611
|
+
*
|
|
612
|
+
* This wrapper automatically applies configuration defaults and checks
|
|
613
|
+
* the enabled flag before delegating to the core restoreVersion function.
|
|
614
|
+
*
|
|
615
|
+
* @param projectRoot - Absolute path to project root
|
|
616
|
+
* @param collection - Collection name
|
|
617
|
+
* @param contentId - Content item ID (slug)
|
|
618
|
+
* @param versionId - Version ID to restore
|
|
619
|
+
* @param contentFilePath - Absolute path to the current content file
|
|
620
|
+
* @param config - Partial version history configuration
|
|
621
|
+
* @param options - Restore options
|
|
622
|
+
* @returns Result of the restore operation
|
|
623
|
+
*
|
|
624
|
+
* @example
|
|
625
|
+
* ```typescript
|
|
626
|
+
* const result = await restoreVersionWithConfig(
|
|
627
|
+
* '/project',
|
|
628
|
+
* 'blog',
|
|
629
|
+
* 'my-post',
|
|
630
|
+
* '2024-12-11T10-30-00-000Z',
|
|
631
|
+
* '/project/src/content/blog/my-post.md'
|
|
632
|
+
* );
|
|
633
|
+
* ```
|
|
447
634
|
*/
|
|
448
|
-
declare function
|
|
635
|
+
declare function restoreVersionWithConfig(projectRoot: string, collection: string, contentId: string, versionId: string, contentFilePath: string, config?: VersionHistoryConfig, options?: RestoreVersionOptions): Promise<RestoreResult>;
|
|
449
636
|
|
|
450
637
|
/**
|
|
451
638
|
* @fileoverview Version history management for content files
|
|
@@ -702,591 +889,404 @@ declare function getVersions(projectRoot: string, collection: string, contentId:
|
|
|
702
889
|
/**
|
|
703
890
|
* Get a specific version with full content.
|
|
704
891
|
*
|
|
705
|
-
* Reads the version file and parses it to return structured data
|
|
706
|
-
* with frontmatter and body separated.
|
|
707
|
-
*
|
|
708
|
-
* @param projectRoot - Absolute path to project root
|
|
709
|
-
* @param collection - Collection name
|
|
710
|
-
* @param contentId - Content item ID (slug)
|
|
711
|
-
* @param versionId - Version ID to retrieve
|
|
712
|
-
* @param config - Version history configuration
|
|
713
|
-
* @returns Full version data or null if not found
|
|
714
|
-
*
|
|
715
|
-
* @example
|
|
716
|
-
* ```typescript
|
|
717
|
-
* const version = await getVersion(
|
|
718
|
-
* '/project',
|
|
719
|
-
* 'blog',
|
|
720
|
-
* 'my-post',
|
|
721
|
-
* '2024-12-11T10-30-00-000Z',
|
|
722
|
-
* { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
|
|
723
|
-
* );
|
|
724
|
-
*
|
|
725
|
-
* if (version) {
|
|
726
|
-
* console.log(`Title: ${version.frontmatter.title}`);
|
|
727
|
-
* console.log(`Body: ${version.body}`);
|
|
728
|
-
* }
|
|
729
|
-
* ```
|
|
730
|
-
*/
|
|
731
|
-
declare function getVersion(projectRoot: string, collection: string, contentId: string, versionId: string, config: Required<VersionHistoryConfig>): Promise<Version | null>;
|
|
732
|
-
/**
|
|
733
|
-
* Delete a specific version.
|
|
734
|
-
*
|
|
735
|
-
* Removes the version file from the filesystem and updates the manifest.
|
|
736
|
-
*
|
|
737
|
-
* @param projectRoot - Absolute path to project root
|
|
738
|
-
* @param collection - Collection name
|
|
739
|
-
* @param contentId - Content item ID (slug)
|
|
740
|
-
* @param versionId - Version ID to delete
|
|
741
|
-
* @param config - Version history configuration
|
|
742
|
-
* @returns Result of the delete operation
|
|
743
|
-
*
|
|
744
|
-
* @example
|
|
745
|
-
* ```typescript
|
|
746
|
-
* const result = await deleteVersion(
|
|
747
|
-
* '/project',
|
|
748
|
-
* 'blog',
|
|
749
|
-
* 'my-post',
|
|
750
|
-
* '2024-12-11T10-30-00-000Z',
|
|
751
|
-
* { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
|
|
752
|
-
* );
|
|
753
|
-
*
|
|
754
|
-
* if (result.success) {
|
|
755
|
-
* console.log('Version deleted');
|
|
756
|
-
* }
|
|
757
|
-
* ```
|
|
758
|
-
*/
|
|
759
|
-
declare function deleteVersion(projectRoot: string, collection: string, contentId: string, versionId: string, config: Required<VersionHistoryConfig>): Promise<VersionResult>;
|
|
760
|
-
/**
|
|
761
|
-
* Clear all versions for a content item.
|
|
762
|
-
*
|
|
763
|
-
* Deletes all version files and resets the manifest to empty state.
|
|
764
|
-
*
|
|
765
|
-
* @param projectRoot - Absolute path to project root
|
|
766
|
-
* @param collection - Collection name
|
|
767
|
-
* @param contentId - Content item ID (slug)
|
|
768
|
-
* @param config - Version history configuration
|
|
769
|
-
* @returns Result of the clear operation
|
|
770
|
-
*
|
|
771
|
-
* @example
|
|
772
|
-
* ```typescript
|
|
773
|
-
* const result = await clearVersions(
|
|
774
|
-
* '/project',
|
|
775
|
-
* 'blog',
|
|
776
|
-
* 'my-post',
|
|
777
|
-
* { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
|
|
778
|
-
* );
|
|
779
|
-
*
|
|
780
|
-
* if (result.success) {
|
|
781
|
-
* console.log('All versions cleared');
|
|
782
|
-
* }
|
|
783
|
-
* ```
|
|
784
|
-
*/
|
|
785
|
-
declare function clearVersions(projectRoot: string, collection: string, contentId: string, config: Required<VersionHistoryConfig>): Promise<VersionResult>;
|
|
786
|
-
declare function pruneVersions(projectRoot: string, collection: string, contentId: string, config: Required<VersionHistoryConfig>): Promise<VersionResult>;
|
|
787
|
-
/**
|
|
788
|
-
* Restore a version to current content.
|
|
789
|
-
*
|
|
790
|
-
* This function:
|
|
791
|
-
* 1. Creates a safety snapshot of the current content before restoring
|
|
792
|
-
* 2. Reads the version content to restore
|
|
793
|
-
* 3. Overwrites the current content file with the version content
|
|
794
|
-
*
|
|
795
|
-
* Note: Cache invalidation should be handled by the caller (API route)
|
|
796
|
-
* since the cache is managed at the server level.
|
|
797
|
-
*
|
|
798
|
-
* @param projectRoot - Absolute path to project root
|
|
799
|
-
* @param collection - Collection name
|
|
800
|
-
* @param contentId - Content item ID (slug)
|
|
801
|
-
* @param versionId - Version ID to restore
|
|
802
|
-
* @param contentFilePath - Absolute path to the current content file
|
|
803
|
-
* @param config - Version history configuration
|
|
804
|
-
* @param options - Restore options
|
|
805
|
-
* @returns Result of the restore operation
|
|
806
|
-
*
|
|
807
|
-
* @example
|
|
808
|
-
* ```typescript
|
|
809
|
-
* const result = await restoreVersion(
|
|
810
|
-
* '/project',
|
|
811
|
-
* 'blog',
|
|
812
|
-
* 'my-post',
|
|
813
|
-
* '2024-12-11T10-30-00-000Z',
|
|
814
|
-
* '/project/src/content/blog/my-post.md',
|
|
815
|
-
* { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
|
|
816
|
-
* );
|
|
817
|
-
*
|
|
818
|
-
* if (result.success) {
|
|
819
|
-
* console.log('Restored content:', result.content);
|
|
820
|
-
* if (result.safetySnapshot) {
|
|
821
|
-
* console.log('Safety snapshot created:', result.safetySnapshot.id);
|
|
822
|
-
* }
|
|
823
|
-
* }
|
|
824
|
-
* ```
|
|
825
|
-
*/
|
|
826
|
-
declare function restoreVersion(projectRoot: string, collection: string, contentId: string, versionId: string, contentFilePath: string, config: Required<VersionHistoryConfig>, options?: RestoreVersionOptions): Promise<RestoreResult>;
|
|
827
|
-
|
|
828
|
-
/**
|
|
829
|
-
* @fileoverview Config-aware wrappers for version history operations
|
|
830
|
-
*
|
|
831
|
-
* This module provides wrapper functions that automatically apply configuration
|
|
832
|
-
* defaults and check the enabled flag before performing version operations.
|
|
833
|
-
* These wrappers simplify usage by accepting partial configuration and handling
|
|
834
|
-
* all the configuration resolution internally.
|
|
835
|
-
*
|
|
836
|
-
* @module @writenex/astro/filesystem/version-config
|
|
837
|
-
* @see {@link saveVersion} - Core save function
|
|
838
|
-
* @see {@link getVersions} - Core list function
|
|
839
|
-
*/
|
|
840
|
-
|
|
841
|
-
/**
|
|
842
|
-
* Resolve version history configuration with defaults applied.
|
|
843
|
-
*
|
|
844
|
-
* Takes a partial configuration and merges it with defaults to produce
|
|
845
|
-
* a complete configuration object.
|
|
846
|
-
*
|
|
847
|
-
* @param config - Partial version history configuration
|
|
848
|
-
* @returns Complete configuration with all defaults applied
|
|
849
|
-
*
|
|
850
|
-
* @example
|
|
851
|
-
* ```typescript
|
|
852
|
-
* const resolved = resolveVersionConfig({ maxVersions: 50 });
|
|
853
|
-
* // Returns: { enabled: true, maxVersions: 50, storagePath: '.writenex/versions' }
|
|
854
|
-
* ```
|
|
855
|
-
*/
|
|
856
|
-
declare function resolveVersionConfig(config?: VersionHistoryConfig): Required<VersionHistoryConfig>;
|
|
857
|
-
/**
|
|
858
|
-
* Check if version history is enabled in the configuration.
|
|
859
|
-
*
|
|
860
|
-
* @param config - Version history configuration (partial or full)
|
|
861
|
-
* @returns True if version history is enabled
|
|
862
|
-
*
|
|
863
|
-
* @example
|
|
864
|
-
* ```typescript
|
|
865
|
-
* if (isVersionHistoryEnabled({ enabled: false })) {
|
|
866
|
-
* // This won't execute
|
|
867
|
-
* }
|
|
868
|
-
* ```
|
|
869
|
-
*/
|
|
870
|
-
declare function isVersionHistoryEnabled(config?: VersionHistoryConfig): boolean;
|
|
871
|
-
/**
|
|
872
|
-
* Save a version with automatic configuration resolution.
|
|
873
|
-
*
|
|
874
|
-
* This wrapper automatically applies configuration defaults and checks
|
|
875
|
-
* the enabled flag before delegating to the core saveVersion function.
|
|
876
|
-
*
|
|
877
|
-
* @param projectRoot - Absolute path to project root
|
|
878
|
-
* @param collection - Collection name
|
|
879
|
-
* @param contentId - Content item ID (slug)
|
|
880
|
-
* @param content - Full markdown content to save
|
|
881
|
-
* @param config - Partial version history configuration
|
|
882
|
-
* @param options - Save options
|
|
883
|
-
* @returns Result of the save operation
|
|
884
|
-
*
|
|
885
|
-
* @example
|
|
886
|
-
* ```typescript
|
|
887
|
-
* // With partial config - defaults are applied automatically
|
|
888
|
-
* const result = await saveVersionWithConfig(
|
|
889
|
-
* '/project',
|
|
890
|
-
* 'blog',
|
|
891
|
-
* 'my-post',
|
|
892
|
-
* '---\ntitle: My Post\n---\n\nContent...',
|
|
893
|
-
* { maxVersions: 50 } // enabled and storagePath use defaults
|
|
894
|
-
* );
|
|
895
|
-
* ```
|
|
896
|
-
*/
|
|
897
|
-
declare function saveVersionWithConfig(projectRoot: string, collection: string, contentId: string, content: string, config?: VersionHistoryConfig, options?: SaveVersionOptions): Promise<VersionResult>;
|
|
898
|
-
/**
|
|
899
|
-
* Get all versions with automatic configuration resolution.
|
|
900
|
-
*
|
|
901
|
-
* This wrapper automatically applies configuration defaults and checks
|
|
902
|
-
* the enabled flag before delegating to the core getVersions function.
|
|
903
|
-
*
|
|
904
|
-
* @param projectRoot - Absolute path to project root
|
|
905
|
-
* @param collection - Collection name
|
|
906
|
-
* @param contentId - Content item ID (slug)
|
|
907
|
-
* @param config - Partial version history configuration
|
|
908
|
-
* @returns Array of version entries (empty if disabled)
|
|
909
|
-
*
|
|
910
|
-
* @example
|
|
911
|
-
* ```typescript
|
|
912
|
-
* const versions = await getVersionsWithConfig(
|
|
913
|
-
* '/project',
|
|
914
|
-
* 'blog',
|
|
915
|
-
* 'my-post',
|
|
916
|
-
* { storagePath: 'custom/versions' }
|
|
917
|
-
* );
|
|
918
|
-
* ```
|
|
919
|
-
*/
|
|
920
|
-
declare function getVersionsWithConfig(projectRoot: string, collection: string, contentId: string, config?: VersionHistoryConfig): Promise<VersionEntry[]>;
|
|
921
|
-
/**
|
|
922
|
-
* Get a specific version with automatic configuration resolution.
|
|
923
|
-
*
|
|
924
|
-
* This wrapper automatically applies configuration defaults and checks
|
|
925
|
-
* the enabled flag before delegating to the core getVersion function.
|
|
926
|
-
*
|
|
927
|
-
* @param projectRoot - Absolute path to project root
|
|
928
|
-
* @param collection - Collection name
|
|
929
|
-
* @param contentId - Content item ID (slug)
|
|
930
|
-
* @param versionId - Version ID to retrieve
|
|
931
|
-
* @param config - Partial version history configuration
|
|
932
|
-
* @returns Full version data or null if not found/disabled
|
|
933
|
-
*
|
|
934
|
-
* @example
|
|
935
|
-
* ```typescript
|
|
936
|
-
* const version = await getVersionWithConfig(
|
|
937
|
-
* '/project',
|
|
938
|
-
* 'blog',
|
|
939
|
-
* 'my-post',
|
|
940
|
-
* '2024-12-11T10-30-00-000Z'
|
|
941
|
-
* );
|
|
942
|
-
* ```
|
|
943
|
-
*/
|
|
944
|
-
declare function getVersionWithConfig(projectRoot: string, collection: string, contentId: string, versionId: string, config?: VersionHistoryConfig): Promise<Version | null>;
|
|
945
|
-
/**
|
|
946
|
-
* Delete a version with automatic configuration resolution.
|
|
947
|
-
*
|
|
948
|
-
* This wrapper automatically applies configuration defaults before
|
|
949
|
-
* delegating to the core deleteVersion function.
|
|
892
|
+
* Reads the version file and parses it to return structured data
|
|
893
|
+
* with frontmatter and body separated.
|
|
950
894
|
*
|
|
951
895
|
* @param projectRoot - Absolute path to project root
|
|
952
896
|
* @param collection - Collection name
|
|
953
897
|
* @param contentId - Content item ID (slug)
|
|
954
|
-
* @param versionId - Version ID to
|
|
955
|
-
* @param config -
|
|
956
|
-
* @returns
|
|
898
|
+
* @param versionId - Version ID to retrieve
|
|
899
|
+
* @param config - Version history configuration
|
|
900
|
+
* @returns Full version data or null if not found
|
|
957
901
|
*
|
|
958
902
|
* @example
|
|
959
903
|
* ```typescript
|
|
960
|
-
* const
|
|
904
|
+
* const version = await getVersion(
|
|
961
905
|
* '/project',
|
|
962
906
|
* 'blog',
|
|
963
907
|
* 'my-post',
|
|
964
|
-
* '2024-12-11T10-30-00-000Z'
|
|
908
|
+
* '2024-12-11T10-30-00-000Z',
|
|
909
|
+
* { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
|
|
965
910
|
* );
|
|
911
|
+
*
|
|
912
|
+
* if (version) {
|
|
913
|
+
* console.log(`Title: ${version.frontmatter.title}`);
|
|
914
|
+
* console.log(`Body: ${version.body}`);
|
|
915
|
+
* }
|
|
966
916
|
* ```
|
|
967
917
|
*/
|
|
968
|
-
declare function
|
|
918
|
+
declare function getVersion(projectRoot: string, collection: string, contentId: string, versionId: string, config: Required<VersionHistoryConfig>): Promise<Version | null>;
|
|
969
919
|
/**
|
|
970
|
-
*
|
|
920
|
+
* Delete a specific version.
|
|
971
921
|
*
|
|
972
|
-
*
|
|
973
|
-
* delegating to the core clearVersions function.
|
|
922
|
+
* Removes the version file from the filesystem and updates the manifest.
|
|
974
923
|
*
|
|
975
924
|
* @param projectRoot - Absolute path to project root
|
|
976
925
|
* @param collection - Collection name
|
|
977
926
|
* @param contentId - Content item ID (slug)
|
|
978
|
-
* @param
|
|
979
|
-
* @
|
|
927
|
+
* @param versionId - Version ID to delete
|
|
928
|
+
* @param config - Version history configuration
|
|
929
|
+
* @returns Result of the delete operation
|
|
980
930
|
*
|
|
981
931
|
* @example
|
|
982
932
|
* ```typescript
|
|
983
|
-
* const result = await
|
|
933
|
+
* const result = await deleteVersion(
|
|
984
934
|
* '/project',
|
|
985
935
|
* 'blog',
|
|
986
|
-
* 'my-post'
|
|
936
|
+
* 'my-post',
|
|
937
|
+
* '2024-12-11T10-30-00-000Z',
|
|
938
|
+
* { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
|
|
987
939
|
* );
|
|
940
|
+
*
|
|
941
|
+
* if (result.success) {
|
|
942
|
+
* console.log('Version deleted');
|
|
943
|
+
* }
|
|
988
944
|
* ```
|
|
989
945
|
*/
|
|
990
|
-
declare function
|
|
946
|
+
declare function deleteVersion(projectRoot: string, collection: string, contentId: string, versionId: string, config: Required<VersionHistoryConfig>): Promise<VersionResult>;
|
|
991
947
|
/**
|
|
992
|
-
*
|
|
948
|
+
* Clear all versions for a content item.
|
|
993
949
|
*
|
|
994
|
-
*
|
|
995
|
-
* delegating to the core pruneVersions function. Uses the configured
|
|
996
|
-
* maxVersions value for determining how many versions to keep.
|
|
950
|
+
* Deletes all version files and resets the manifest to empty state.
|
|
997
951
|
*
|
|
998
952
|
* @param projectRoot - Absolute path to project root
|
|
999
953
|
* @param collection - Collection name
|
|
1000
954
|
* @param contentId - Content item ID (slug)
|
|
1001
|
-
* @param config -
|
|
1002
|
-
* @returns Result of the
|
|
955
|
+
* @param config - Version history configuration
|
|
956
|
+
* @returns Result of the clear operation
|
|
1003
957
|
*
|
|
1004
958
|
* @example
|
|
1005
959
|
* ```typescript
|
|
1006
|
-
*
|
|
1007
|
-
* const result = await pruneVersionsWithConfig(
|
|
960
|
+
* const result = await clearVersions(
|
|
1008
961
|
* '/project',
|
|
1009
962
|
* 'blog',
|
|
1010
963
|
* 'my-post',
|
|
1011
|
-
* { maxVersions:
|
|
964
|
+
* { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
|
|
1012
965
|
* );
|
|
966
|
+
*
|
|
967
|
+
* if (result.success) {
|
|
968
|
+
* console.log('All versions cleared');
|
|
969
|
+
* }
|
|
1013
970
|
* ```
|
|
1014
971
|
*/
|
|
1015
|
-
declare function
|
|
972
|
+
declare function clearVersions(projectRoot: string, collection: string, contentId: string, config: Required<VersionHistoryConfig>): Promise<VersionResult>;
|
|
973
|
+
declare function pruneVersions(projectRoot: string, collection: string, contentId: string, config: Required<VersionHistoryConfig>): Promise<VersionResult>;
|
|
1016
974
|
/**
|
|
1017
|
-
* Restore a version
|
|
975
|
+
* Restore a version to current content.
|
|
1018
976
|
*
|
|
1019
|
-
* This
|
|
1020
|
-
*
|
|
977
|
+
* This function:
|
|
978
|
+
* 1. Creates a safety snapshot of the current content before restoring
|
|
979
|
+
* 2. Reads the version content to restore
|
|
980
|
+
* 3. Overwrites the current content file with the version content
|
|
981
|
+
*
|
|
982
|
+
* Note: Cache invalidation should be handled by the caller (API route)
|
|
983
|
+
* since the cache is managed at the server level.
|
|
1021
984
|
*
|
|
1022
985
|
* @param projectRoot - Absolute path to project root
|
|
1023
986
|
* @param collection - Collection name
|
|
1024
987
|
* @param contentId - Content item ID (slug)
|
|
1025
988
|
* @param versionId - Version ID to restore
|
|
1026
989
|
* @param contentFilePath - Absolute path to the current content file
|
|
1027
|
-
* @param config -
|
|
990
|
+
* @param config - Version history configuration
|
|
1028
991
|
* @param options - Restore options
|
|
1029
992
|
* @returns Result of the restore operation
|
|
1030
993
|
*
|
|
1031
994
|
* @example
|
|
1032
995
|
* ```typescript
|
|
1033
|
-
* const result = await
|
|
996
|
+
* const result = await restoreVersion(
|
|
1034
997
|
* '/project',
|
|
1035
998
|
* 'blog',
|
|
1036
999
|
* 'my-post',
|
|
1037
1000
|
* '2024-12-11T10-30-00-000Z',
|
|
1038
|
-
* '/project/src/content/blog/my-post.md'
|
|
1001
|
+
* '/project/src/content/blog/my-post.md',
|
|
1002
|
+
* { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' }
|
|
1039
1003
|
* );
|
|
1004
|
+
*
|
|
1005
|
+
* if (result.success) {
|
|
1006
|
+
* console.log('Restored content:', result.content);
|
|
1007
|
+
* if (result.safetySnapshot) {
|
|
1008
|
+
* console.log('Safety snapshot created:', result.safetySnapshot.id);
|
|
1009
|
+
* }
|
|
1010
|
+
* }
|
|
1040
1011
|
* ```
|
|
1041
1012
|
*/
|
|
1042
|
-
declare function
|
|
1013
|
+
declare function restoreVersion(projectRoot: string, collection: string, contentId: string, versionId: string, contentFilePath: string, config: Required<VersionHistoryConfig>, options?: RestoreVersionOptions): Promise<RestoreResult>;
|
|
1043
1014
|
|
|
1044
1015
|
/**
|
|
1045
|
-
* @fileoverview
|
|
1016
|
+
* @fileoverview File watcher for detecting external changes
|
|
1046
1017
|
*
|
|
1047
|
-
* This module provides
|
|
1048
|
-
*
|
|
1018
|
+
* This module provides file watching capabilities to detect when
|
|
1019
|
+
* content files are modified outside of the Writenex editor
|
|
1020
|
+
* (e.g., in VS Code or another editor).
|
|
1049
1021
|
*
|
|
1050
|
-
*
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
*
|
|
1022
|
+
* @module @writenex/astro/filesystem/watcher
|
|
1023
|
+
*/
|
|
1024
|
+
/**
|
|
1025
|
+
* File change event types
|
|
1026
|
+
*/
|
|
1027
|
+
type FileChangeType = "add" | "change" | "unlink";
|
|
1028
|
+
/**
|
|
1029
|
+
* File change event
|
|
1030
|
+
*/
|
|
1031
|
+
interface FileChangeEvent {
|
|
1032
|
+
type: FileChangeType;
|
|
1033
|
+
path: string;
|
|
1034
|
+
collection: string;
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Watcher options
|
|
1038
|
+
*/
|
|
1039
|
+
interface WatcherOptions {
|
|
1040
|
+
/** Callback when a file changes */
|
|
1041
|
+
onChange?: (event: FileChangeEvent) => void;
|
|
1042
|
+
/** Debounce delay in milliseconds */
|
|
1043
|
+
debounceMs?: number;
|
|
1044
|
+
/** Patterns to ignore */
|
|
1045
|
+
ignored?: string[];
|
|
1046
|
+
}
|
|
1047
|
+
/**
|
|
1048
|
+
* Content file watcher
|
|
1054
1049
|
*
|
|
1055
|
-
*
|
|
1050
|
+
* Watches the src/content directory for changes and emits events
|
|
1051
|
+
* when files are added, modified, or deleted.
|
|
1052
|
+
*/
|
|
1053
|
+
declare class ContentWatcher {
|
|
1054
|
+
private watcher;
|
|
1055
|
+
private projectRoot;
|
|
1056
|
+
private contentDir;
|
|
1057
|
+
private options;
|
|
1058
|
+
private debounceTimers;
|
|
1059
|
+
constructor(projectRoot: string, contentDir?: string, options?: WatcherOptions);
|
|
1060
|
+
/**
|
|
1061
|
+
* Start watching for file changes
|
|
1062
|
+
*/
|
|
1063
|
+
start(): void;
|
|
1064
|
+
/**
|
|
1065
|
+
* Stop watching for file changes
|
|
1066
|
+
*/
|
|
1067
|
+
stop(): Promise<void>;
|
|
1068
|
+
/**
|
|
1069
|
+
* Handle a file change event
|
|
1070
|
+
*/
|
|
1071
|
+
private handleChange;
|
|
1072
|
+
/**
|
|
1073
|
+
* Emit a file change event
|
|
1074
|
+
*/
|
|
1075
|
+
private emitChange;
|
|
1076
|
+
/**
|
|
1077
|
+
* Check if the watcher is running
|
|
1078
|
+
*/
|
|
1079
|
+
isWatching(): boolean;
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Track file modification times for conflict detection
|
|
1083
|
+
*/
|
|
1084
|
+
declare class FileModificationTracker {
|
|
1085
|
+
private mtimes;
|
|
1086
|
+
/**
|
|
1087
|
+
* Record the current modification time of a file
|
|
1088
|
+
*/
|
|
1089
|
+
track(filePath: string): Promise<void>;
|
|
1090
|
+
/**
|
|
1091
|
+
* Check if a file has been modified externally
|
|
1092
|
+
*/
|
|
1093
|
+
hasExternalChanges(filePath: string): Promise<boolean>;
|
|
1094
|
+
/**
|
|
1095
|
+
* Clear tracking for a file
|
|
1096
|
+
*/
|
|
1097
|
+
untrack(filePath: string): void;
|
|
1098
|
+
/**
|
|
1099
|
+
* Clear all tracking
|
|
1100
|
+
*/
|
|
1101
|
+
clear(): void;
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Create a content watcher instance
|
|
1056
1105
|
*/
|
|
1106
|
+
declare function createContentWatcher(projectRoot: string, options?: WatcherOptions): ContentWatcher;
|
|
1057
1107
|
|
|
1058
1108
|
/**
|
|
1059
|
-
*
|
|
1109
|
+
* @fileoverview Filesystem writer for content operations
|
|
1110
|
+
*
|
|
1111
|
+
* This module provides functions for creating, updating, and deleting
|
|
1112
|
+
* content files in Astro content collections.
|
|
1113
|
+
*
|
|
1114
|
+
* ## Features:
|
|
1115
|
+
* - Create new content files with frontmatter
|
|
1116
|
+
* - Update existing content files
|
|
1117
|
+
* - Delete content files
|
|
1118
|
+
* - Generate unique slugs to avoid collisions
|
|
1119
|
+
* - Support for different file patterns (flat, folder-based, date-prefixed)
|
|
1120
|
+
* - Automatic version history creation before updates
|
|
1121
|
+
*
|
|
1122
|
+
* @module @writenex/astro/filesystem/writer
|
|
1060
1123
|
*/
|
|
1061
|
-
|
|
1124
|
+
|
|
1062
1125
|
/**
|
|
1063
|
-
*
|
|
1126
|
+
* Options for creating content
|
|
1064
1127
|
*/
|
|
1065
|
-
interface
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1128
|
+
interface CreateContentOptions {
|
|
1129
|
+
/** Frontmatter data */
|
|
1130
|
+
frontmatter: Record<string, unknown>;
|
|
1131
|
+
/** Markdown body content */
|
|
1132
|
+
body: string;
|
|
1133
|
+
/** Custom slug (optional, generated from title if not provided) */
|
|
1134
|
+
slug?: string;
|
|
1135
|
+
/** File pattern for the collection (e.g., "{slug}/index.md", "{date}-{slug}.md") */
|
|
1136
|
+
filePattern?: string;
|
|
1137
|
+
/** Custom token values to override automatic resolution */
|
|
1138
|
+
customTokens?: Record<string, string>;
|
|
1073
1139
|
}
|
|
1074
1140
|
/**
|
|
1075
|
-
* Options for
|
|
1141
|
+
* Options for updating content
|
|
1076
1142
|
*/
|
|
1077
|
-
interface
|
|
1078
|
-
/**
|
|
1079
|
-
|
|
1080
|
-
/**
|
|
1081
|
-
|
|
1082
|
-
/**
|
|
1083
|
-
|
|
1084
|
-
/**
|
|
1085
|
-
|
|
1086
|
-
/**
|
|
1087
|
-
|
|
1088
|
-
/**
|
|
1089
|
-
|
|
1143
|
+
interface UpdateContentOptions {
|
|
1144
|
+
/** Updated frontmatter data */
|
|
1145
|
+
frontmatter?: Record<string, unknown>;
|
|
1146
|
+
/** Updated markdown body content */
|
|
1147
|
+
body?: string;
|
|
1148
|
+
/** Project root for version history (required for version creation) */
|
|
1149
|
+
projectRoot?: string;
|
|
1150
|
+
/** Collection name for version history */
|
|
1151
|
+
collection?: string;
|
|
1152
|
+
/** Version history configuration */
|
|
1153
|
+
versionHistoryConfig?: Required<VersionHistoryConfig>;
|
|
1154
|
+
/**
|
|
1155
|
+
* Expected modification time for conflict detection.
|
|
1156
|
+
* If provided and the file's mtime differs, the update will fail with a conflict error.
|
|
1157
|
+
*/
|
|
1158
|
+
expectedMtime?: number;
|
|
1090
1159
|
}
|
|
1091
1160
|
/**
|
|
1092
|
-
*
|
|
1093
|
-
*
|
|
1094
|
-
* @param filename - Original filename
|
|
1095
|
-
* @returns True if valid image file
|
|
1161
|
+
* Result of a write operation
|
|
1096
1162
|
*/
|
|
1097
|
-
|
|
1163
|
+
interface WriteResult {
|
|
1164
|
+
/** Whether the operation was successful */
|
|
1165
|
+
success: boolean;
|
|
1166
|
+
/** The content ID (slug) */
|
|
1167
|
+
id?: string;
|
|
1168
|
+
/** The file path */
|
|
1169
|
+
path?: string;
|
|
1170
|
+
/** Error message if failed */
|
|
1171
|
+
error?: string;
|
|
1172
|
+
/** New modification time after write (for conflict detection) */
|
|
1173
|
+
mtime?: number;
|
|
1174
|
+
/** Conflict error if update failed due to external modification */
|
|
1175
|
+
conflict?: ContentConflictError;
|
|
1176
|
+
}
|
|
1098
1177
|
/**
|
|
1099
|
-
*
|
|
1100
|
-
*
|
|
1101
|
-
* @param options - Upload options
|
|
1102
|
-
* @returns Upload result with paths
|
|
1103
|
-
*
|
|
1104
|
-
* @example
|
|
1105
|
-
* ```typescript
|
|
1106
|
-
* const result = await uploadImage({
|
|
1107
|
-
* filename: "hero.jpg",
|
|
1108
|
-
* data: imageBuffer,
|
|
1109
|
-
* collection: "blog",
|
|
1110
|
-
* contentId: "my-post",
|
|
1111
|
-
* projectRoot: "/path/to/project",
|
|
1112
|
-
* });
|
|
1178
|
+
* Generate a URL-safe slug from a string
|
|
1113
1179
|
*
|
|
1114
|
-
*
|
|
1115
|
-
*
|
|
1116
|
-
* }
|
|
1117
|
-
* ```
|
|
1180
|
+
* @param text - Text to slugify
|
|
1181
|
+
* @returns URL-safe slug
|
|
1118
1182
|
*/
|
|
1119
|
-
declare function
|
|
1183
|
+
declare function generateSlug(text: string): string;
|
|
1120
1184
|
/**
|
|
1121
|
-
*
|
|
1122
|
-
*
|
|
1123
|
-
* Simple parser for multipart/form-data with single file upload.
|
|
1124
|
-
* For production, consider using a proper multipart parser library.
|
|
1185
|
+
* Generate a unique slug that doesn't conflict with existing files
|
|
1125
1186
|
*
|
|
1126
|
-
* @param
|
|
1127
|
-
* @param
|
|
1128
|
-
* @
|
|
1129
|
-
|
|
1130
|
-
declare function parseMultipartFormData(body: Buffer, contentType: string): {
|
|
1131
|
-
file?: {
|
|
1132
|
-
filename: string;
|
|
1133
|
-
data: Buffer;
|
|
1134
|
-
contentType: string;
|
|
1135
|
-
};
|
|
1136
|
-
fields: Record<string, string>;
|
|
1137
|
-
};
|
|
1138
|
-
/**
|
|
1139
|
-
* Content structure type detected from file path
|
|
1140
|
-
*/
|
|
1141
|
-
type ContentStructure = "flat" | "folder-based" | "date-prefixed";
|
|
1142
|
-
/**
|
|
1143
|
-
* Result of content structure detection
|
|
1187
|
+
* @param baseSlug - The base slug to start with
|
|
1188
|
+
* @param collectionPath - Path to the collection directory
|
|
1189
|
+
* @param filePattern - File pattern (default: "{slug}.md")
|
|
1190
|
+
* @returns A unique slug
|
|
1144
1191
|
*/
|
|
1145
|
-
|
|
1146
|
-
/** Detected structure type */
|
|
1147
|
-
structure: ContentStructure;
|
|
1148
|
-
/** Path to the image folder (null if doesn't exist) */
|
|
1149
|
-
imageFolderPath: string | null;
|
|
1150
|
-
}
|
|
1192
|
+
declare function generateUniqueSlug(baseSlug: string, collectionPath: string, filePattern?: string): Promise<string>;
|
|
1151
1193
|
/**
|
|
1152
|
-
*
|
|
1194
|
+
* Create a new content file in a collection
|
|
1153
1195
|
*
|
|
1154
|
-
*
|
|
1155
|
-
* -
|
|
1156
|
-
* -
|
|
1157
|
-
* -
|
|
1196
|
+
* Supports various file patterns with automatic token resolution:
|
|
1197
|
+
* - `{slug}.md` - Simple flat structure (default)
|
|
1198
|
+
* - `{slug}/index.md` - Folder-based content
|
|
1199
|
+
* - `{date}-{slug}.md` - Date-prefixed naming
|
|
1200
|
+
* - `{year}/{slug}.md` - Year folder structure
|
|
1201
|
+
* - `{year}/{month}/{slug}.md` - Year/month folder structure
|
|
1202
|
+
* - `{year}/{month}/{day}/{slug}.md` - Full date folder structure
|
|
1203
|
+
* - `{lang}/{slug}.md` - Language-prefixed (i18n)
|
|
1204
|
+
* - `{category}/{slug}.md` - Category folder structure
|
|
1205
|
+
* - `{author}/{slug}.md` - Author folder structure
|
|
1206
|
+
* - Any custom pattern with tokens from frontmatter
|
|
1207
|
+
*
|
|
1208
|
+
* Token resolution priority:
|
|
1209
|
+
* 1. Custom tokens (explicitly provided via customTokens)
|
|
1210
|
+
* 2. Known token resolvers (date, year, month, lang, category, etc.)
|
|
1211
|
+
* 3. Frontmatter values (for custom tokens)
|
|
1212
|
+
* 4. Default values
|
|
1158
1213
|
*
|
|
1159
1214
|
* @param collectionPath - Absolute path to the collection directory
|
|
1160
|
-
* @param
|
|
1161
|
-
* @
|
|
1162
|
-
* @returns Path to the image folder, or null if no image folder exists
|
|
1215
|
+
* @param options - Content creation options
|
|
1216
|
+
* @returns WriteResult with success status and file info
|
|
1163
1217
|
*
|
|
1164
1218
|
* @example
|
|
1165
1219
|
* ```typescript
|
|
1166
|
-
* // Flat
|
|
1167
|
-
* const
|
|
1168
|
-
* '
|
|
1169
|
-
* '
|
|
1170
|
-
*
|
|
1171
|
-
* );
|
|
1172
|
-
* // Returns: '/project/src/content/blog/my-post' (if exists)
|
|
1220
|
+
* // Flat structure
|
|
1221
|
+
* const result = await createContent('/project/src/content/blog', {
|
|
1222
|
+
* frontmatter: { title: 'My New Post', pubDate: new Date() },
|
|
1223
|
+
* body: '# Hello World',
|
|
1224
|
+
* });
|
|
1173
1225
|
*
|
|
1174
1226
|
* // Folder-based structure
|
|
1175
|
-
* const
|
|
1176
|
-
* '
|
|
1177
|
-
* '
|
|
1178
|
-
* '/
|
|
1179
|
-
* );
|
|
1180
|
-
* // Returns: '/project/src/content/blog/my-post'
|
|
1181
|
-
* ```
|
|
1182
|
-
*/
|
|
1183
|
-
declare function getContentImageFolder(collectionPath: string, contentId: string, contentFilePath: string): string | null;
|
|
1184
|
-
/**
|
|
1185
|
-
* Detect the content structure type from a content file path
|
|
1186
|
-
*
|
|
1187
|
-
* @param contentFilePath - Absolute path to the content file
|
|
1188
|
-
* @returns The detected content structure type
|
|
1189
|
-
*/
|
|
1190
|
-
declare function detectContentStructure(contentFilePath: string): ContentStructure;
|
|
1191
|
-
/**
|
|
1192
|
-
* Options for recursive directory scanning
|
|
1193
|
-
*/
|
|
1194
|
-
interface ScanOptions {
|
|
1195
|
-
/** Maximum recursion depth */
|
|
1196
|
-
maxDepth: number;
|
|
1197
|
-
/** Current recursion depth */
|
|
1198
|
-
currentDepth: number;
|
|
1199
|
-
/** Base path for calculating relative paths */
|
|
1200
|
-
basePath: string;
|
|
1201
|
-
}
|
|
1202
|
-
/**
|
|
1203
|
-
* Scan a directory recursively for image files
|
|
1204
|
-
*
|
|
1205
|
-
* Recursively scans the given directory for image files with supported extensions.
|
|
1206
|
-
* Skips hidden folders (starting with .) and special folders (starting with _).
|
|
1207
|
-
* Limits recursion to the specified maxDepth.
|
|
1227
|
+
* const result = await createContent('/project/src/content/blog', {
|
|
1228
|
+
* frontmatter: { title: 'My New Post', pubDate: new Date() },
|
|
1229
|
+
* body: '# Hello World',
|
|
1230
|
+
* filePattern: '{slug}/index.md',
|
|
1231
|
+
* });
|
|
1208
1232
|
*
|
|
1209
|
-
*
|
|
1210
|
-
*
|
|
1211
|
-
*
|
|
1212
|
-
*
|
|
1233
|
+
* // i18n structure with custom token
|
|
1234
|
+
* const result = await createContent('/project/src/content/blog', {
|
|
1235
|
+
* frontmatter: { title: 'My New Post', lang: 'id' },
|
|
1236
|
+
* body: '# Hello World',
|
|
1237
|
+
* filePattern: '{lang}/{slug}.md',
|
|
1238
|
+
* });
|
|
1213
1239
|
*
|
|
1214
|
-
*
|
|
1215
|
-
*
|
|
1216
|
-
*
|
|
1217
|
-
* '
|
|
1218
|
-
* '/
|
|
1219
|
-
*
|
|
1220
|
-
* );
|
|
1240
|
+
* // Custom pattern with explicit token
|
|
1241
|
+
* const result = await createContent('/project/src/content/blog', {
|
|
1242
|
+
* frontmatter: { title: 'My New Post' },
|
|
1243
|
+
* body: '# Hello World',
|
|
1244
|
+
* filePattern: '{category}/{slug}.md',
|
|
1245
|
+
* customTokens: { category: 'tutorials' },
|
|
1246
|
+
* });
|
|
1221
1247
|
* ```
|
|
1222
1248
|
*/
|
|
1223
|
-
declare function
|
|
1249
|
+
declare function createContent(collectionPath: string, options: CreateContentOptions): Promise<WriteResult>;
|
|
1224
1250
|
/**
|
|
1225
|
-
*
|
|
1251
|
+
* Update an existing content file
|
|
1226
1252
|
*
|
|
1227
|
-
*
|
|
1228
|
-
*
|
|
1229
|
-
*
|
|
1253
|
+
* Creates a version snapshot of the current content before updating
|
|
1254
|
+
* when version history is configured. Skips both version creation and
|
|
1255
|
+
* file write if the new content is identical to the current file content.
|
|
1256
|
+
* Version creation errors are logged but do not fail the save operation.
|
|
1230
1257
|
*
|
|
1231
|
-
* @param
|
|
1232
|
-
* @param
|
|
1233
|
-
* @
|
|
1258
|
+
* @param filePath - Absolute path to the content file
|
|
1259
|
+
* @param collectionPath - Path to the collection directory
|
|
1260
|
+
* @param options - Update options including version history config
|
|
1261
|
+
* @returns WriteResult with success status
|
|
1234
1262
|
*
|
|
1235
1263
|
* @example
|
|
1236
1264
|
* ```typescript
|
|
1237
|
-
*
|
|
1238
|
-
* const relPath = calculateRelativePath(
|
|
1265
|
+
* const result = await updateContent(
|
|
1239
1266
|
* '/project/src/content/blog/my-post.md',
|
|
1240
|
-
* '/project/src/content/blog
|
|
1241
|
-
*
|
|
1242
|
-
*
|
|
1243
|
-
*
|
|
1244
|
-
*
|
|
1245
|
-
*
|
|
1246
|
-
*
|
|
1247
|
-
*
|
|
1248
|
-
* );
|
|
1249
|
-
* // Returns: './images/hero.jpg'
|
|
1250
|
-
*
|
|
1251
|
-
* // Nested subfolder
|
|
1252
|
-
* const relPath = calculateRelativePath(
|
|
1253
|
-
* '/project/src/content/blog/my-post/index.md',
|
|
1254
|
-
* '/project/src/content/blog/my-post/assets/photos/hero.jpg'
|
|
1267
|
+
* '/project/src/content/blog',
|
|
1268
|
+
* {
|
|
1269
|
+
* frontmatter: { title: 'Updated Title' },
|
|
1270
|
+
* body: '# Updated Content',
|
|
1271
|
+
* projectRoot: '/project',
|
|
1272
|
+
* collection: 'blog',
|
|
1273
|
+
* versionHistoryConfig: { enabled: true, maxVersions: 20, storagePath: '.writenex/versions' },
|
|
1274
|
+
* }
|
|
1255
1275
|
* );
|
|
1256
|
-
* // Returns: './assets/photos/hero.jpg'
|
|
1257
1276
|
* ```
|
|
1258
1277
|
*/
|
|
1259
|
-
declare function
|
|
1278
|
+
declare function updateContent(filePath: string, collectionPath: string, options: UpdateContentOptions): Promise<WriteResult>;
|
|
1260
1279
|
/**
|
|
1261
|
-
*
|
|
1262
|
-
*
|
|
1263
|
-
* This is the main entry point for image discovery. It:
|
|
1264
|
-
* 1. Locates the content file using getContentFilePath
|
|
1265
|
-
* 2. Determines the image folder using getContentImageFolder
|
|
1266
|
-
* 3. Scans the folder recursively using scanDirectoryForImages
|
|
1267
|
-
* 4. Calculates relative paths for all discovered images
|
|
1280
|
+
* Delete a content file
|
|
1268
1281
|
*
|
|
1269
|
-
* @param
|
|
1270
|
-
* @
|
|
1271
|
-
* @param options - Optional discovery options
|
|
1272
|
-
* @returns ImageDiscoveryResult with discovered images or error
|
|
1282
|
+
* @param filePath - Absolute path to the content file
|
|
1283
|
+
* @returns WriteResult with success status
|
|
1273
1284
|
*
|
|
1274
1285
|
* @example
|
|
1275
1286
|
* ```typescript
|
|
1276
|
-
* const result = await
|
|
1277
|
-
* '/project/src/content/blog',
|
|
1278
|
-
* 'my-post',
|
|
1279
|
-
* { maxDepth: 3 }
|
|
1280
|
-
* );
|
|
1281
|
-
*
|
|
1282
|
-
* if (result.success) {
|
|
1283
|
-
* console.log(`Found ${result.images.length} images`);
|
|
1284
|
-
* for (const img of result.images) {
|
|
1285
|
-
* console.log(`- ${img.relativePath}`);
|
|
1286
|
-
* }
|
|
1287
|
-
* }
|
|
1287
|
+
* const result = await deleteContent('/project/src/content/blog/my-post.md');
|
|
1288
1288
|
* ```
|
|
1289
1289
|
*/
|
|
1290
|
-
declare function
|
|
1290
|
+
declare function deleteContent(filePath: string): Promise<WriteResult>;
|
|
1291
1291
|
|
|
1292
1292
|
export { type ContentStructure, type ContentStructureResult, ContentWatcher, type CreateContentOptions, DEFAULT_IMAGE_CONFIG, type FileChangeEvent, type FileChangeType, FileModificationTracker, type ImageUploadOptions, type ImageUploadResult, type ReadContentOptions, type ReadFileResult, type UpdateContentOptions, type WatcherOptions, type WriteResult, calculateRelativePath, checkCollection, clearVersions, clearVersionsWithConfig, createContent, createContentWatcher, createEmptyManifest, deleteContent, deleteVersion, deleteVersionWithConfig, detectContentStructure, discoverContentImages, ensureGitignore, ensureStorageDirectory, extractSlug, generateExcerpt, generatePreview, generateSlug, generateUniqueSlug, generateVersionId, getCollectionCount, getCollectionSummaries, getContentFilePath, getContentImageFolder, getFileStats, getManifestPath, getOrRecoverManifest, getVersion, getVersionFilePath, getVersionStoragePath, getVersionWithConfig, getVersions, getVersionsWithConfig, isContentFile, isValidImageFile, isVersionHistoryEnabled, parseMultipartFormData, parseVersionId, pruneVersions, pruneVersionsWithConfig, readCollection, readContentFile, readManifest, recoverManifest, resolveVersionConfig, restoreVersion, restoreVersionWithConfig, saveVersion, saveVersionWithConfig, scanDirectoryForImages, toContentSummary, updateContent, uploadImage, writeManifest };
|